PressPrinterImpl.mesa
Copyright © 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
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: 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;
};
};
IsAPressFile: PUBLIC PROC [fileName: ROPE] RETURNS [BOOLEAN] = {
buffer: REF TEXTNEW[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: ROPENIL
] RETURNS [handle: Handle] = {
pages: INT ← -1;
sendingMessage: ROPE ← Rope.Cat["Sending ", fileName, " to ", server];
buffer: REF TEXTNEW[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 {
What a hack!
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: STREAMNIL;
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: BOOLEANFALSE;
maybePDFile: BOOLEANTRUE;
maybePressFile: BOOLEANTRUE;
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;
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[];
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;
fill in the document directory
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: BOOLEANTRUE] = {
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: BOOLEANTRUE] = {
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.
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.