DIRECTORY EFTPDefs, IO, FileIO, LongString, PressFormat, PressPrinter, Process, PupDefs, PupStream, PupTypes, Rope; PressPrinterImpl: CEDAR MONITOR IMPORTS EFTPDefs, LongString, IO, FileIO, Process, PupDefs, PupStream, 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 Process.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 Process.Aborted; }; }; Byte: TYPE = [0..256); Block: TYPE = PACKED ARRAY [0..bytesPerPressRecord] OF Byte; -- oversize for overrun check IsAPressFile: PUBLIC PROCEDURE [fileName: ROPE] RETURNS [BOOLEAN _ FALSE] = TRUSTED { buffer: REF Block _ NEW[Block]; unsafeBlock: IO.UnsafeBlock; ReadABlock: UNSAFE PROCEDURE = { nBytesRead: INT; buffer[bytesPerPressRecord] _ 123; nBytesRead _ stream.UnsafeGetBlock[unsafeBlock]; IF buffer[bytesPerPressRecord] # 123 THEN ERROR; -- it went too far! IF nBytesRead # bytesPerPressRecord THEN ERROR; -- should always be reading full blocks }; lengthInBytes: INT; stream: STREAM; documentDirectory: LONG POINTER TO PressFormat.DDV; documentDirectory _ unsafeBlock.base _ LOOPHOLE[buffer]; unsafeBlock.startIndex _ 0; unsafeBlock.stopIndexPlusOne _ bytesPerPressRecord; stream _ FileIO.Open[fileName, read ! FileIO.OpenFailed => TRUSTED {GOTO Quit} ]; lengthInBytes _ stream.GetLength[]; IF lengthInBytes > 22 AND stream.GetChar = LOOPHOLE[0AAH, CHAR] AND stream.GetChar = LOOPHOLE[0AAH, CHAR] THEN RETURN[TRUE]; -- it looks like a PD file IF lengthInBytes = 0 OR (lengthInBytes MOD bytesPerPressRecord # 0) THEN RETURN; stream.SetIndex[lengthInBytes-bytesPerPressRecord]; ReadABlock[]; stream.Close; IF documentDirectory.Passwd # PressFormat.PressPasswd THEN RETURN; IF documentDirectory.nRecs # lengthInBytes/bytesPerPressRecord THEN RETURN; RETURN[TRUE]; EXITS Quit => {} }; SendPressFile: PUBLIC ENTRY PROCEDURE [ fileName: ROPE, server: ROPE, copies: INT _ 1, progressProc: PressPrinter.ProgressProc _ NIL, userName: ROPE _ NIL ] RETURNS [handle: Handle] = TRUSTED { ENABLE {ABORTED => GOTO Quit; UNWIND => EFTPDefs.EFTPAbortSending[""]}; sendingMessage: ROPE _ Rope.Cat["Sending ", fileName, " to ", server]; buffer: REF Block _ NEW[Block]; unsafeBlock: IO.UnsafeBlock; ReadABlock: UNSAFE PROCEDURE = { nBytesRead: INT; buffer[bytesPerPressRecord] _ 123; nBytesRead _ stream.UnsafeGetBlock[unsafeBlock]; IF buffer[bytesPerPressRecord] # 123 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] _ 0; nBytesRead _ nBytesRead + 1; ENDLOOP; IF aPDFile AND handle.currentPage = 0 THEN { copiesFieldOffset: CARDINAL = 10; -- see the PD file description. pdCopies: LONG POINTER TO CARDINAL = LOOPHOLE[buffer, LONG POINTER] + copiesFieldOffset*SIZE[CARDINAL]; pdCopies^ _ MIN[MAX[copies, 0], LAST[NAT]]; }; }; }; lengthInBytes: INT; stream: STREAM; Oops: PROCEDURE [state: State, message: ROPE] = TRUSTED { Progress[handle, state, fileName.Concat[message]]; IF stream # NIL THEN stream.Close[]; }; pupHandle: PupHandle; documentDirectory: LONG POINTER TO PressFormat.DDV; aPDFile: BOOLEAN _ FALSE; maybePDFile: BOOLEAN _ TRUE; maybePressFile: BOOLEAN _ TRUE; documentDirectory _ unsafeBlock.base _ LOOPHOLE[buffer]; unsafeBlock.startIndex _ 0; unsafeBlock.stopIndexPlusOne _ bytesPerPressRecord; handle _ NEW[PressPrinterRec]; handle.progressProc _ progressProc; stream _ FileIO.Open[fileName, read ! FileIO.OpenFailed => TRUSTED {Oops[unableToOpenFile, ": unable to open file"]; GOTO Quit} ]; lengthInBytes _ stream.GetLength[]; IF lengthInBytes <= 22 THEN maybePDFile _ FALSE; IF lengthInBytes < bytesPerPressRecord THEN maybePressFile _ FALSE; maybePDFile _ maybePDFile AND stream.GetChar = LOOPHOLE[0AAH, CHAR] AND stream.GetChar = LOOPHOLE[0AAH, CHAR]; maybePressFile _ maybePressFile AND lengthInBytes MOD bytesPerPressRecord = 0; handle.totalNumberOfPages _ (lengthInBytes+(bytesPerPressRecord-1)) / bytesPerPressRecord; IF maybePressFile THEN { stream.SetIndex[lengthInBytes-bytesPerPressRecord]; ReadABlock[]; maybePressFile _ maybePressFile AND documentDirectory.Passwd = PressFormat.PressPasswd AND documentDirectory.nRecs = handle.totalNumberOfPages; }; IF maybePressFile AND maybePDFile THEN { Oops[invalidPressFile, ": Press / PD ambiguity"]; RETURN }; IF maybePressFile OR maybePDFile THEN aPDFile _ maybePDFile ELSE { Oops[invalidPressFile, ": not a valid Press or PD file"]; RETURN }; stream.SetIndex[0]; Progress[handle, opening, Rope.Concat["Opening connection with ", server]]; pupHandle _ CreatePupHandle[]; 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"]; stream.Close; GO TO LeaveQuietly} ENDCASE; Process.Yield[]; ENDLOOP; handle.currentPage _ 0; Progress[handle, transmitting, sendingMessage]; FOR i: INT IN [1..handle.totalNumberOfPages) DO ReadABlock[]; IF NOT SuccessfullySendUnsafeBlock[pupHandle, unsafeBlock] THEN { Progress[handle, serverTrouble, "Server trouble"]; stream.Close; GOTO Quit; }; handle.currentPage _ i; Progress[handle, transmitting, sendingMessage]; ENDLOOP; ReadABlock[]; IF NOT aPDFile THEN { 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; }; IF NOT SuccessfullySendUnsafeBlock[pupHandle, unsafeBlock] THEN { Progress[handle, serverTrouble, "Server trouble"]; stream.Close; GOTO Quit; }; handle.currentPage _ handle.totalNumberOfPages; Progress[handle, transmitting, sendingMessage]; CloseConnection[pupHandle]; Progress[handle, done, fileName.Concat[" sent to "].Concat[server]]; stream.Close; EXITS Quit => {EFTPDefs.EFTPAbortSending[""]}; 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 [ trouble: PupTrouble _ none ]; CreatePupHandle: UNSAFE PROCEDURE [] RETURNS [pupHandle: PupHandle] = UNCHECKED { pupHandle _ NEW[PupRec]; PupDefs.PupPackageMake[]; }; OpenConnection: UNSAFE PROCEDURE [pupHandle: PupHandle, server: ROPE] RETURNS [success: BOOLEAN _ TRUE] = UNCHECKED {OPEN EFTPDefs; pupAddress: PupDefs.PupAddress _ [, , PupTypes.eftpReceiveSoc]; shortName: STRING = [40]; pupHandle.trouble _ none; LongString.AppendString[shortName, LOOPHOLE[server.ToRefText[]]]; PupDefs.GetPupAddress[@pupAddress, shortName ! PupStream.PupNameTrouble => {success _ FALSE; pupHandle.trouble _ nameNotFound; CONTINUE}]; IF success THEN EFTPOpenForSending[pupAddress ! EFTPTimeOut => {success _ FALSE; pupHandle.trouble _ timeout; CONTINUE}; EFTPTroubleSending => { success _ FALSE; IF e = eftpReceiverBusyAbort THEN pupHandle.trouble _ busy ELSE pupHandle.trouble _ troubleSending; CONTINUE }; ]; }; SuccessfullySendUnsafeBlock: UNSAFE PROCEDURE [ pupHandle: PupHandle, unsafeBlock: IO.UnsafeBlock ] RETURNS [success: BOOLEAN _ TRUE] = UNCHECKED {OPEN EFTPDefs; pupHandle.trouble _ none; IF unsafeBlock.startIndex # 0 THEN ERROR; EFTPSendBlock[unsafeBlock.base, unsafeBlock.stopIndexPlusOne ! EFTPTimeOut => {success _ FALSE; pupHandle.trouble _ timeout; CONTINUE}; EFTPTroubleSending => {success _ FALSE; pupHandle.trouble _ troubleSending; CONTINUE}; ]; }; CloseConnection: UNSAFE PROCEDURE [pupHandle: PupHandle] = UNCHECKED { EFTPDefs.EFTPFinishSending[]; PupDefs.PupPackageDestroy[]; }; END. ~PressPrinterImpl.mesa Michael Plass, September 12, 1983 2:58 pm Last Edited by: Beach, June 7, 1983 4:39 pm 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, September 12, 1983 2:57 pm: Added stream.Close statements to IsAPressFile and SendPressFile Κ ο– "cedar" style˜JšΟc™Jš*™*J™+J˜šΟk ˜ J˜ Jšžœ˜J˜J˜ J˜ J˜ J˜J˜J˜ J˜ J˜J˜—šœžœž˜Jšžœžœ+˜KJšžœ˜—Jšž˜J˜Jšžœžœžœžœ˜Jšžœžœžœ˜Jšœžœ˜!J˜Jšœžœ˜$Jšœžœ˜$J˜Jšœžœžœžœ˜*šœžœžœžœ˜'J˜Jšœžœžœ˜ Jšœžœ˜Jšœ žœ˜J˜'J˜J˜—šΟn œžœž œžœ ˜CJšžœ˜J˜J˜—š Ÿœžœž œžœžœ˜IJšžœ˜#J˜J˜—š Ÿœžœž œžœžœ˜`šœžœžœ˜EJšžœ5˜9—J˜J˜—šŸœžœž œ˜,J˜J˜J˜—šŸœž œ.žœžœ˜QJšžœžœžœ˜šžœžœ˜'J˜4J˜Jšžœ˜J˜—J˜J˜—Jšœžœ ˜Jš œžœžœžœžœ˜ZJ˜šŸ œžœž œ žœžœžœžœžœ˜UJšœžœ žœ˜Jšœ žœ ˜šŸ œžœ˜ Jšœ žœ˜J˜"J˜0Jšžœ#žœžœ˜DJšžœ"žœžœ'˜WJ˜—Jšœžœ˜Jšœžœ˜Jš œžœžœžœ žœ˜3Jšœ'žœ ˜8J˜J˜3˜%Jšœžœžœ˜(J˜—J˜#Jšžœžœžœžœžœžœžœžœžœžœ˜—Jš žœžœžœžœžœ˜PJ˜3J˜ Jšœ ˜ Jšžœ4žœžœ˜BJšžœ=žœžœ˜KJšžœžœ˜ Jšžœ ˜J˜J˜—šŸ œžœžœž œ˜'Jšœ žœ˜Jšœžœ˜ Jšœžœ˜Jšœ*žœ˜.Jšœ žœž˜Jšœžœžœ˜&Jšžœžœžœžœ#˜GJšœžœ2˜FJšœžœ žœ˜Jšœ žœ ˜šŸ œžœ˜ Jšœ žœ˜J˜"J˜0Jšžœ#žœžœ˜DJš žœžœ žœ"žœžœ8˜xšžœ žœ˜Jšžœ"žœ2žœžœ˜dšžœ"ž˜)Jšœ4˜4Jšžœ˜—šžœ žœžœ˜,J™ Jšœžœ˜AJšœ žœžœžœžœžœ žœžœžœžœ˜gJš œ žœžœ žœžœ˜+Jšœ˜—Jšœ˜—J˜—Jšœžœ˜Jšœžœ˜šŸœž œžœžœ˜9J˜2Jšžœ žœžœ˜$J˜—J˜Jš œžœžœžœ žœ˜3Jšœ žœžœ˜Jšœ žœžœ˜Jšœžœžœ˜Jšœ'žœ ˜8J˜J˜3Jšœ žœ˜J˜#˜%Jšœžœ3žœ˜YJ˜—J˜#Jšžœžœžœ˜0Jšžœ%žœžœ˜Cšœ žœ ž˜Jšœžœžœž˜)Jšœžœžœ˜&—Jšœ žœžœ˜NJ˜Zšžœžœ˜J˜3J˜ Jšœ žœ4žœ5˜Jšœ˜—šžœžœ žœ˜(Jšœ2ž˜8Jšœ˜—Jšžœžœ žœ˜;šžœ˜Jšœ:ž˜@Jšœ˜—J˜J˜KJ˜šžœ#ž˜*šžœž˜J˜4J˜OJ˜EJšœXžœžœ˜mJšžœ˜—J˜Jšžœ˜—J˜J˜/šžœžœžœ ž˜/J˜ šžœžœ5žœ˜AJ˜2Jšœ ˜ Jšžœ˜ J˜—J˜J˜/Jšžœ˜—J˜ šžœžœ žœ˜Jšžœ4žœžœ˜AJš™Jšœžœ+˜AJš œ žœžœ žœžœ˜(J˜J˜!J˜—šžœžœ5žœ˜AJ˜2J˜ Jšžœ˜ J˜—J˜/J˜/J˜J˜DJ˜ šž˜J˜(Jšœž˜—J˜J˜—Jš œžœžœžœ žœžœ˜2J˜šŸ œžœž œžœžœžœžœž œ˜`Jšœžœ˜,Jšœ žœ žœ˜!Jš žœžœžœ žœžœ˜AJ˜J˜—Jšœ žœ7˜GJ˜Jšœ žœžœ˜šœžœžœ˜J˜J˜J˜—šŸœžœ˜$Jšžœž œ˜,Jšœ žœ ˜J˜J˜J˜—šŸœžœ žœ˜EJš žœ žœžœž œžœ ˜=J˜?Jšœ žœ˜J˜Jšœ#žœ˜AJšœVžœ$žœ˜Ššžœ ž˜šœ˜Jšœžœžœ˜H˜Jšœ žœ˜Jšžœžœ˜:Jšžœ$˜(Jšž˜J˜——J˜—J˜J˜—šŸœžœ˜/J˜Jšœ žœ ˜Jš œžœ žœžœž œžœ ˜?J˜Jšžœžœžœ˜)˜>Jšœžœžœ˜HJšœ!žœ&žœ˜VJ˜—J˜J˜—šŸœžœž œ˜FJ˜J˜J˜J˜—Jšžœ˜J˜J™0J˜J™:J™