PressPrinterImpl.mesa
Michael Plass, November 15, 1983 11:40 am
DIRECTORY
EFTPDefs,
FS,
IO,
PressFormat,
PressPrinter,
Process,
PupDefs,
PupStream,
PupTypes,
Rope;
PressPrinterImpl: CEDAR MONITOR
IMPORTS EFTPDefs, FS, IO, 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: ROPENIL,
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: ROPENIL] = {
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;
};
};
Byte: TYPE = [0..256);
Block: TYPE = PACKED ARRAY [0..bytesPerPressRecord] OF Byte; -- oversize for overrun check
IsAPressFile: PUBLIC PROCEDURE [fileName: ROPE] RETURNS [BOOLEAN] = TRUSTED {
buffer: REF Block ← NEW[Block];
unsafeBlock: IO.UnsafeBlock ← [base: NIL, startIndex: 0, count: bytesPerPressRecord];
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;
unsafeBlock.base ← LOOPHOLE[buffer];
documentDirectory ← LOOPHOLE[buffer];
stream ← FS.StreamOpen[fileName !
FS.Error => TRUSTED {GOTO Quit}
];
lengthInBytes ← stream.GetLength[];
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];
IF documentDirectory.Passwd # PressFormat.PressPasswd THEN RETURN[FALSE];
IF documentDirectory.nRecs # lengthInBytes/bytesPerPressRecord THEN RETURN[FALSE];
RETURN[TRUE];
EXITS Quit => {RETURN[FALSE]}
};
SendPressFile: PUBLIC ENTRY PROCEDURE [
fileName: ROPE,
server: ROPE,
copies: INT ← 1,
progressProc: PressPrinter.ProgressProc ← NIL,
userName: ROPENIL
] RETURNS [handle: Handle] = TRUSTED {
ENABLE {UNWIND => EFTPDefs.EFTPAbortSending[""]};
sendingMessage: ROPE ← Rope.Cat["Sending ", fileName, " to ", server];
buffer: REF Block ← NEW[Block];
unsafeBlock: IO.UnsafeBlock ← [base: NIL, startIndex: 0, count: bytesPerPressRecord];
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 {
What a hack!
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: STREAMNIL;
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: BOOLEANFALSE;
maybePDFile: BOOLEANTRUE;
maybePressFile: BOOLEANTRUE;
unsafeBlock.base ← LOOPHOLE[buffer];
documentDirectory ← LOOPHOLE[buffer];
handle ← NEW[PressPrinterRec];
handle.progressProc ← progressProc;
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;
maybePDFilemaybePDFile 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[];
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]];
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"]; IO.Close[stream]; 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"];
IO.Close[stream];
GOTO Quit;
};
handle.currentPage ← i;
Progress[handle, transmitting, sendingMessage];
ENDLOOP;
ReadABlock[];
IF NOT aPDFile THEN {
IF documentDirectory.Passwd # PressFormat.PressPasswd THEN ERROR;
fill in the document directory
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"];
IO.Close[stream];
GOTO Quit;
};
handle.currentPage ← handle.totalNumberOfPages;
Progress[handle, transmitting, sendingMessage];
CloseConnection[pupHandle];
Progress[handle, done, fileName.Concat[" sent to "].Concat[server]];
IO.Close[stream];
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: BOOLEANTRUE] = UNCHECKED {OPEN EFTPDefs;
pupAddress: PupDefs.PupAddress;
pupHandle.trouble ← none;
pupAddress ← PupDefs.GetPupAddress[PupTypes.eftpReceiveSoc, server !
PupStream.PupNameTrouble => {success ← FALSE; pupHandle.trouble ← nameNotFound; CONTINUE}];
pupHandle.trouble ← none;
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: BOOLEANTRUE] = UNCHECKED {OPEN EFTPDefs;
pupHandle.trouble ← none;
IF unsafeBlock.startIndex # 0 THEN ERROR;
EFTPSendBlock[unsafeBlock.base, unsafeBlock.count !
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.
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.