DIRECTORY Convert USING [RopeFromInt], EFTP USING [Abort, CreateSender, Finish, Handle, PutBlock, Timeout, Trouble], FS USING [Error, StreamOpen], IO USING [Close, GetBlock, GetChar, GetLength, SetIndex, STREAM], PressFormat USING [DDV, PressPasswd], PressPrinter USING [Handle, PressPrinterRec, ProgressProc, State], Process USING [Pause, SecondsToTicks], Pup USING [Address], PupName USING [Error, NameLookup], PupWKS USING [eftp], Rope USING [Cat, Concat, Fetch, Length, ROPE]; PressPrinterImpl: CEDAR PROGRAM IMPORTS Convert, EFTP, FS, IO, Process, PupName, Rope EXPORTS PressPrinter = BEGIN STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; State: TYPE = PressPrinter.State; wordsPerPressRecord: CARDINAL = 256; bytesPerPressRecord: CARDINAL = 512; Handle: PUBLIC TYPE = REF PressPrinterRec; PressPrinterRec: PUBLIC TYPE = RECORD [ currentState: State _ queued, currentStateMessage: ROPE _ NIL, totalNumberOfPages: INT _ 0, currentPage: INT _ 0, progressProc: PressPrinter.ProgressProc ]; CurrentState: PUBLIC PROCEDURE [handle: Handle] RETURNS [State] = { RETURN[handle.currentState]; }; CurrentStateMessage: PUBLIC PROCEDURE [handle: Handle] RETURNS [ROPE] = { RETURN[handle.currentStateMessage]; }; CurrentProgress: PUBLIC PROCEDURE [handle: Handle] RETURNS [fractionOfFileTransmitted: REAL] = { fractionOfFileTransmitted _ IF handle.totalNumberOfPages = 0 THEN 1.0 ELSE handle.currentPage/(handle.totalNumberOfPages + 0.0) }; Abort: PUBLIC PROCEDURE [handle: Handle] = { handle.currentState _ aborted; }; SetState: PROCEDURE [handle: Handle, state: State, stateMessage: ROPE] = INLINE { IF handle.currentState = aborted THEN ERROR ABORTED; handle.currentState _ state; handle.currentStateMessage _ stateMessage; }; Progress: PROCEDURE [handle: Handle, state: State, stateMessage: ROPE _ NIL] = { SetState[handle, state, stateMessage]; IF handle.progressProc # NIL THEN handle.progressProc[handle]; IF handle.currentState = aborted THEN { handle.currentStateMessage _ "Transmission aborted"; handle.progressProc[handle]; ERROR ABORTED; }; }; IsAPressFile: PUBLIC PROC [fileName: ROPE] RETURNS [BOOLEAN] = { buffer: REF TEXT _ NEW[TEXT[bytesPerPressRecord+1]]; ReadABlock: PROC = { nBytesRead: INT; buffer[bytesPerPressRecord] _ '$; nBytesRead _ IO.GetBlock[stream, buffer, 0, bytesPerPressRecord]; IF buffer[bytesPerPressRecord] # '$ THEN ERROR; -- it went too far! IF nBytesRead # bytesPerPressRecord THEN ERROR; -- should always be reading full blocks }; lengthInBytes: INT; stream: STREAM; stream _ FS.StreamOpen[fileName ! FS.Error => TRUSTED {GOTO Quit} ]; lengthInBytes _ IO.GetLength[stream]; IF lengthInBytes > 22 AND stream.GetChar = VAL[0AAH] AND stream.GetChar = VAL[0AAH] THEN {IO.Close[stream]; RETURN[TRUE]}; -- it looks like a PD file IF lengthInBytes = 0 OR (lengthInBytes MOD bytesPerPressRecord # 0) THEN {IO.Close[stream]; RETURN[FALSE]}; stream.SetIndex[lengthInBytes-bytesPerPressRecord]; ReadABlock[]; IO.Close[stream]; TRUSTED { ddv: LONG POINTER TO PressFormat.DDV; ddv _ LOOPHOLE[buffer, LONG POINTER]+TEXT[0].SIZE; IF ddv.Passwd # PressFormat.PressPasswd THEN RETURN[FALSE]; IF ddv.nRecs # lengthInBytes/bytesPerPressRecord THEN RETURN[FALSE]; }; RETURN[TRUE]; EXITS Quit => {RETURN[FALSE]} }; pauseTime: INT _ 4; SendPressFile: PUBLIC PROCEDURE [ fileName: ROPE, server: ROPE, copies: INT _ 1, progressProc: PressPrinter.ProgressProc _ NIL, userName: ROPE _ NIL ] RETURNS [handle: Handle] = { pages: INT _ -1; sendingMessage: ROPE _ Rope.Cat["Sending ", fileName, " to ", server]; buffer: REF TEXT _ NEW[TEXT[bytesPerPressRecord+1]]; ReadABlock: PROC = { nBytesRead: INT; buffer[bytesPerPressRecord] _ '$; nBytesRead _ IO.GetBlock[stream, buffer, 0, bytesPerPressRecord]; IF buffer[bytesPerPressRecord] # '$ THEN ERROR; -- it went too far! IF NOT aPDFile AND nBytesRead # bytesPerPressRecord THEN ERROR; -- should always be reading full blocks for a press file IF aPDFile THEN { IF nBytesRead # bytesPerPressRecord AND handle.currentPage # handle.totalNumberOfPages-1 THEN ERROR; WHILE nBytesRead < bytesPerPressRecord DO buffer[nBytesRead] _ 0C; nBytesRead _ nBytesRead + 1; ENDLOOP; IF aPDFile AND handle.currentPage = 0 THEN TRUSTED { copiesFieldOffset: CARDINAL = 10; -- see the PD file description. pdCopies: LONG POINTER TO CARDINAL; pdCopies _ LOOPHOLE[buffer, LONG POINTER]+TEXT[0].SIZE; pdCopies _ pdCopies + copiesFieldOffset*SIZE[CARDINAL]; pdCopies^ _ MIN[MAX[copies, 0], LAST[NAT]]; }; }; }; lengthInBytes: INT; stream: STREAM _ NIL; Oops: PROCEDURE [state: State, message: ROPE] = TRUSTED { Progress[handle, state, fileName.Concat[message]]; IF stream # NIL THEN stream.Close[]; }; pupHandle: PupHandle _ NEW[PupRec]; documentDirectory: LONG POINTER TO PressFormat.DDV; aPDFile: BOOLEAN _ FALSE; maybePDFile: BOOLEAN _ TRUE; maybePressFile: BOOLEAN _ TRUE; TRUSTED { documentDirectory _ LOOPHOLE[buffer, LONG POINTER]+TEXT[0].SIZE; }; handle _ NEW[PressPrinterRec]; TRUSTED { handle.progressProc _ progressProc; }; -- Yetch stream _ FS.StreamOpen[fileName, read ! FS.Error => TRUSTED {Oops[unableToOpenFile, ": unable to open file"]; CONTINUE} ]; IF stream # NIL THEN { ENABLE {ABORTED => {IO.Close[stream]; GOTO Quit}; UNWIND => IO.Close[stream]}; lengthInBytes _ stream.GetLength[]; IF lengthInBytes <= 22 THEN maybePDFile _ FALSE; IF lengthInBytes < bytesPerPressRecord THEN maybePressFile _ FALSE; maybePDFile _ maybePDFile AND stream.GetChar = VAL[0AAH] AND stream.GetChar = VAL[0AAH]; maybePressFile _ maybePressFile AND lengthInBytes MOD bytesPerPressRecord = 0; handle.totalNumberOfPages _ (lengthInBytes+(bytesPerPressRecord-1)) / bytesPerPressRecord; IF maybePressFile THEN { stream.SetIndex[lengthInBytes-bytesPerPressRecord]; ReadABlock[]; TRUSTED { maybePressFile _ maybePressFile AND documentDirectory.Passwd = PressFormat.PressPasswd AND documentDirectory.nRecs = handle.totalNumberOfPages; }; }; IF maybePressFile AND maybePDFile THEN { Oops[invalidPressFile, ": Press / PD ambiguity"]; IO.Close[stream]; RETURN }; IF maybePressFile OR maybePDFile THEN aPDFile _ maybePDFile ELSE { Oops[invalidPressFile, ": not a valid Press or PD file"]; IO.Close[stream]; RETURN }; stream.SetIndex[0]; Progress[handle, opening, Rope.Concat["Opening connection with ", server]]; UNTIL OpenConnection[pupHandle, server] DO SELECT pupHandle.trouble FROM busy => Progress[handle, serverBusy, "Server busy"]; timeout => Progress[handle, serverTimeout, "Server timeout, will keep trying"]; troubleSending => Progress[handle, serverTrouble, "Trouble sending"]; nameNotFound => {Progress[handle, serverTrouble, "Server name not found"]; IO.Close[stream]; GO TO LeaveQuietly}; ENDCASE => {Progress[handle, serverTrouble, "Unknown name lookup error"]; IO.Close[stream]; GO TO LeaveQuietly}; Process.Pause[Process.SecondsToTicks[pauseTime]]; ENDLOOP; handle.currentPage _ 0; Progress[handle, transmitting, sendingMessage]; FOR i: INT IN [1..handle.totalNumberOfPages) DO ReadABlock[]; IF NOT SuccessfullySendBlock[pupHandle, buffer] THEN { Progress[handle, serverTrouble, "Server trouble"]; IO.Close[stream]; GOTO Quit; }; handle.currentPage _ i; Progress[handle, transmitting, sendingMessage]; ENDLOOP; ReadABlock[]; IF NOT aPDFile THEN TRUSTED { IF documentDirectory.Passwd # PressFormat.PressPasswd THEN ERROR; StuffUserName[LOOPHOLE[@(documentDirectory.CreatStr)], userName]; copies _ MIN[MAX[copies, 0], LAST[NAT]]; documentDirectory.fCopy _ 1; documentDirectory.lCopy _ copies; pages _ documentDirectory.nParts-1; }; IF NOT SuccessfullySendBlock[pupHandle, buffer] THEN { Progress[handle, serverTrouble, "Server trouble"]; IO.Close[stream]; GOTO Quit; }; handle.currentPage _ handle.totalNumberOfPages; Progress[handle, transmitting, sendingMessage]; EFTP.Finish[pupHandle.eftp]; {msg: ROPE _ fileName.Concat[" sent to "].Concat[server]; IF copies # 1 THEN { msg _ msg.Cat[" ", Convert.RopeFromInt[copies], " copies"]; }; IF pages >= 0 THEN { msg _ msg.Cat[" ", Convert.RopeFromInt[pages], " page", IF pages#1 THEN "s" ELSE NIL]; }; Progress[handle, done, msg]; }; IO.Close[stream]; EXITS Quit => IF pupHandle.eftp # NIL THEN EFTP.Abort[pupHandle.eftp, "Mumble"]; LeaveQuietly => NULL }; }; BcplUserName: TYPE = PACKED ARRAY [0..32) OF CHAR; StuffUserName: UNSAFE PROCEDURE [dest: LONG POINTER TO BcplUserName, source: ROPE] = UNCHECKED { length: [0..256) _ MIN[source.Length[], 31]; dest[0] _ LOOPHOLE[length, CHAR]; FOR i: NAT IN [0..length) DO dest[i+1] _ source.Fetch[i] ENDLOOP; }; PupTrouble: TYPE = {none, busy, timeout, troubleSending, nameNotFound}; PupHandle: TYPE = REF PupRec; PupRec: TYPE = RECORD [ eftp: EFTP.Handle _ NIL, trouble: PupTrouble _ none ]; OpenConnection: PROC [pupHandle: PupHandle, server: ROPE] RETURNS [success: BOOLEAN _ TRUE] = { pupAddress: Pup.Address; pupHandle.trouble _ none; pupAddress _ PupName.NameLookup[server, PupWKS.eftp ! PupName.Error => {success _ FALSE; pupHandle.trouble _ nameNotFound; CONTINUE}]; IF success THEN pupHandle.eftp _ EFTP.CreateSender[pupAddress ! EFTP.Timeout => {success _ FALSE; pupHandle.trouble _ timeout; CONTINUE}; EFTP.Trouble => { success _ FALSE; IF code = receiverBusyAbort THEN pupHandle.trouble _ busy ELSE pupHandle.trouble _ troubleSending; CONTINUE }; ]; }; SuccessfullySendBlock: PROC [pupHandle: PupHandle, buffer: REF TEXT] RETURNS [success: BOOLEAN _ TRUE] = { pupHandle.trouble _ none; EFTP.PutBlock[pupHandle.eftp, buffer ! EFTP.Timeout => {success _ FALSE; pupHandle.trouble _ timeout; CONTINUE}; EFTP.Trouble => {success _ FALSE; pupHandle.trouble _ troubleSending; CONTINUE}; ]; }; END. PressPrinterImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Michael Plass, April 2, 1985 5:44:41 pm PST Hal Murray, March 13, 1986 2:55:21 am PST What a hack! fill in the document directory Created by Michael Plass, June 15, 1982 3:14 pm First sent press file successfully, June 17, 1982 12:10 pm Michael Plass, November 1, 1982 9:28 am: Added UNWIND, fCopy Michael Plass, April 29, 1983 1:21 pm: Added ability to recognise and send PD files. Edited on June 7, 1983 3:56 pm, by Beach Catch PupStream.PupNameTrouble signal in OpenConnection and SendPressFile. Michael Plass, November 15, 1983 11:40 am Cedar 5.0 conversion. Κ – "cedar" style˜šΟc™Icodešœ Οmœ1™šŸœŸœ˜'J˜4J˜JšŸœŸœ˜J˜—J˜J˜—š   œŸœŸœ ŸœŸœŸœ˜@Jš œŸœŸœŸœŸœ˜4š  œŸœ˜Jšœ Ÿœ˜J˜!Jšœ Ÿœ2˜AJšŸœ"ŸœŸœ˜CJšŸœ"ŸœŸœ'˜WJ˜—JšœŸœ˜JšœŸœ˜˜!Jšœ ŸœŸœ˜J˜—JšœŸœ˜%JšŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜•JšŸœŸœŸœŸœŸœŸœŸœ˜kJ˜3J˜ JšŸœ˜šŸ ˜ Jš œŸœŸœŸœ Ÿœ˜%Jš œŸœ ŸœŸœŸœŸœ˜2JšŸœ&ŸœŸœŸœ˜;JšŸœ/ŸœŸœŸœ˜G—JšŸœŸœ˜ JšŸœ ŸœŸœ˜J˜J˜—Jšœ Ÿœ˜š  œŸœŸ œ˜!Jšœ Ÿœ˜JšœŸœ˜ JšœŸœ˜Jšœ*Ÿœ˜.Jšœ ŸœŸ˜JšœŸœ˜JšœŸœ˜JšœŸœ2˜FJš œŸœŸœŸœŸœ˜4š  œŸœ˜Jšœ Ÿœ˜J˜!Jšœ Ÿœ2˜AJšŸœ"ŸœŸœ˜CJš ŸœŸœ Ÿœ"ŸœŸœ8˜xšŸœ Ÿœ˜JšŸœ"Ÿœ2ŸœŸœ˜dšŸœ"Ÿ˜)Jšœ5˜5JšŸœ˜—šŸœ ŸœŸœŸœ˜4J™ JšœŸœ˜AJš œ ŸœŸœŸœŸœ˜#Jš œ Ÿœ ŸœŸœŸœŸœ˜7Jšœ(ŸœŸœ˜7Jš œ ŸœŸœ ŸœŸœ˜+Jšœ˜—Jšœ˜—J˜—JšœŸœ˜JšœŸœŸœ˜š œŸ œŸœŸœ˜9J˜2JšŸœ ŸœŸœ˜$J˜—JšœŸœ ˜#Jš œŸœŸœŸœ Ÿœ˜3Jšœ ŸœŸœ˜Jšœ ŸœŸœ˜JšœŸœŸœ˜Jš ŸœŸœ ŸœŸœŸœŸœ˜MJšœ Ÿœ˜JšŸœ*˜9˜'JšŸœ Ÿœ3Ÿœ˜OJ˜—šŸœ ŸœŸœ˜Jš ŸœŸœŸœŸœŸœŸœ˜NJ˜#JšŸœŸœŸœ˜0JšŸœ%ŸœŸœ˜Cšœ Ÿœ Ÿ˜JšœŸœŸ˜JšœŸœ˜—Jšœ ŸœŸœ˜NJ˜ZšŸœŸœ˜J˜3J˜ šŸœ˜ Jšœ Ÿœ4Ÿœ8˜’—Jšœ˜—šŸœŸœ Ÿœ˜(Jšœ2ŸœŸ˜JJšœ˜—JšŸœŸœ Ÿœ˜;šŸœ˜Jšœ:ŸœŸ˜RJšœ˜—J˜J˜KšŸœ#Ÿ˜*šŸœŸ˜J˜4J˜OJ˜EJšœKŸœŸœ˜rJšŸœCŸœŸœ˜q—Jšœ1˜1JšŸœ˜—J˜J˜/šŸœŸœŸœ Ÿ˜/J˜ šŸœŸœ*Ÿœ˜6J˜2JšŸœ˜JšŸœ˜ J˜—J˜J˜/JšŸœ˜—J˜ šŸœŸœ ŸœŸœ˜JšŸœ4ŸœŸœ˜AJš™JšœŸœ+˜AJš œ ŸœŸœ ŸœŸœ˜(J˜J˜!J˜#J˜—šŸœŸœ*Ÿœ˜6J˜2JšŸœ˜JšŸœ˜ J˜—J˜/J˜/JšŸœ˜šœŸœ/˜9šŸœ Ÿœ˜Jšœ;˜;Jšœ˜—šŸœ Ÿœ˜Jš œ8Ÿœ ŸœŸœŸœ˜VJšœ˜—Jšœ˜J˜—JšŸœ˜šŸ˜Jš œŸœŸœŸœŸœ!˜JJšœŸ˜—J˜—J˜J˜—Jš œŸœŸœŸœ ŸœŸœ˜2J˜š  œŸœŸ œŸœŸœŸœŸœŸ œ˜`JšœŸœ˜,Jšœ Ÿœ Ÿœ˜!Jš ŸœŸœŸœ ŸœŸœ˜AJ˜J˜—Jšœ Ÿœ7˜GJ˜Jšœ ŸœŸœ˜šœŸœŸœ˜JšœŸœ Ÿœ˜J˜J˜J˜—š œŸœ Ÿœ˜9JšŸœ ŸœŸœ˜%Jšœ˜J˜JšœRŸœ$Ÿœ˜†šŸœ Ÿ˜šœŸœ˜/JšœŸœŸœ˜I˜Jšœ Ÿœ˜JšŸœŸœ˜9JšŸœ$˜(JšŸ˜J˜——J˜—J˜J˜—š œŸœ ŸœŸœ˜DJšŸœ ŸœŸœ˜%J˜šŸœ"˜&JšœŸœŸœ˜IJšœŸœ&Ÿœ˜PJ˜—J˜J˜—JšŸœ˜—J™J™0J™:J™