XNSPSSpoolerImpl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation.  All rights reserved.
Tim Diebert: February 9, 1987 4:10:10 pm PST
 
DIRECTORY
BasicTime USING [Now],
CrRPC USING [BulkDataCheckAbortProc, BulkDataSource, Handle],
FS USING [Delete, Error, StreamOpen],
IO USING [rope, card, Close, PutF, PutFR1, RopeFromROS, ROS, STREAM, Value],
PrintingP4V3Aux USING [ExposePrintAttributes, ExposePrintOptions],
Process USING [Detach],
Rope USING [ROPE],
PrintingP4V3,
XNSPSMessages USING [LogMessage],
XNSPSPrint USING [PrintFile],
XNSPSSpooler;
 
XNSPSSpoolerImpl: 
CEDAR 
MONITOR
IMPORTS BasicTime, FS, IO, PrintingP4V3Aux, Process, XNSPSMessages, XNSPSPrint
EXPORTS XNSPSSpooler, PrintingP4V3 ~ {
OPEN PrintingP4V3;
 
Copied Types
BulkDataSource: TYPE ~ CrRPC.BulkDataSource;
Handle: TYPE ~ CrRPC.Handle;
ROPE: TYPE ~ Rope.ROPE;
 
Global vars
properties: PrintingP4V3.PrinterProperties;
 
PrintQueue
spoolerBusy, formatterBusy, printerBusy: BOOL ← FALSE;
State: TYPE = {empty, spooling, spooled, printing, done, aborted};
maxQueue: CARDINAL = 10;
QueueIndex: TYPE = [0 .. maxQueue);
nextEmpty: QueueIndex ← 0;  -- Next available.
queue: PrintQueue;
queueChanged: CONDITION;
queueCount: INTEGER ← 0;
PrintQueue: TYPE = ARRAY QueueIndex OF PrintQueueEntry;
PrintQueueEntry: 
TYPE = 
RECORD [
id: PrintingP4V3.RequestID ← [0, 0, 0, 0, 0],
fileName: ROPE ← NIL,
state: State ← empty,
printAttributes: PrintingP4V3.PrintAttributes ← NIL,
printOptions: PrintingP4V3.PrintOptions ← NIL
];
 
 
Helper PROCs
ConstructPrinterStatus: 
PROC [spoolerBusy, formatterBusy, printerBusy: 
BOOL]
RETURNS [printerStatus: PrinterStatus] ~ {
printerStatus ← NEW[ PrinterStatusObject[4] ];
TRUSTED {printerStatus.body[0] ← [spooler[IF spoolerBusy THEN busy ELSE available]]};
TRUSTED {printerStatus.body[1] ← [formatter[IF formatterBusy THEN busy ELSE available]]};
TRUSTED {printerStatus.body[2] ← [printer[IF printerBusy THEN busy ELSE available]]};
TRUSTED {printerStatus.body[3] ← [media[ConstructMedia[]]]};
};
 
 
I/O Procs
Logger: 
ENTRY 
PROC [format: Rope.
ROPE ← 
NIL, v1, v2, v3, v4, v5: 
IO.Value ← [null[]],
id: PrintingP4V3.RequestID] ~ {
out: IO.STREAM ← IO.ROS[]; r: ROPE;
IO.PutF[out, format, v1, v2, v3, v4, v5];
r ← IO.RopeFromROS[out];
XNSPSMessages.LogMessage[r, id];
};
 
 
Errors 
Busy: PUBLIC ERROR ~ CODE;
ConnectionError: PUBLIC ERROR [problem: ConnectionProblem] ~ CODE;
InsufficientSpoolSpace: PUBLIC ERROR ~ CODE;
InvalidPrintParameters: PUBLIC ERROR ~ CODE;
MasterTooLarge: PUBLIC ERROR ~ CODE;
MediumUnavailable: PUBLIC ERROR ~ CODE;
ServiceUnavailable: PUBLIC ERROR ~ CODE;
SpoolingDisabled: PUBLIC ERROR ~ CODE;
SpoolingQueueFull: PUBLIC ERROR ~ CODE;
SystemError: PUBLIC ERROR ~ CODE;
TooManyClients: PUBLIC ERROR ~ CODE;
TransferError: PUBLIC ERROR [problem: TransferProblem] ~ CODE;
Undefined: PUBLIC ERROR [problem: CARDINAL] ~ CODE;
 
Active Procs
currentID: PrintingP4V3.RequestID ← [0, 0, 0, 0, 0];
GetNextID: 
ENTRY PROC [] 
RETURNS [id: PrintingP4V3.RequestID] = {
ENABLE UNWIND => NULL;
md: MACHINE DEPENDENT RECORD [lo, hi: CARDINAL]; i: CARD;
md1: MACHINE DEPENDENT RECORD [lo, hi: CARDINAL] ← LOOPHOLE[BasicTime.Now[]];
md.lo ← currentID[4]; md.hi ← currentID[3];
i ← LOOPHOLE[md];
i ← i + 1;
md ← LOOPHOLE[i];
currentID[4] ← md.lo; currentID[3] ← md.hi;
currentID[2] ← md1.lo; currentID[1] ← md1.hi;
RETURN [currentID];
};
 
GetPrinterProperties: 
PUBLIC 
PROC [h: Handle] 
RETURNS [PrinterProperties] ~ {
REPORTS [ServiceUnavailable, SystemError, Undefined]
RETURN[properties];
};
 
ConvertToRequestStatus: 
PROC [s: State] 
RETURNS [PrintingP4V3.InterpressMasterStatus] = {
RETURN [
SELECT s 
FROM
empty => unknown,
spooling => pending,
spooled => pending,
printing => inProgress,
done => completed,
aborted => aborted,
ENDCASE => ERROR];
 
};
 
GetPrintRequestStatus: 
PUBLIC 
PROC [h: Handle, printRequestID: RequestID]
RETURNS [status: RequestStatus] ~ 
TRUSTED {
REPORTS [ServiceUnavailable, SystemError, Undefined] 
FOR i: QueueIndex 
IN QueueIndex 
DO
IF queue[i].id = printRequestID 
THEN {
status ← NEW[RequestStatusObject[1]];
status.body[0] ← [status[ConvertToRequestStatus[queue[i].state]]];
RETURN [status];
};
 
ENDLOOP;
 
status ← NEW[RequestStatusObject[1]];
status.body[0] ← [status[unknown]];
RETURN [status];
};
 
EnumerateQueue: 
PUBLIC 
PROC [
p: 
PROC [id: PrintingP4V3.RequestID, status: PrintingP4V3.InterpressMasterStatus,
printAttributes: PrintingP4V3.PrintAttributes, printOptions: PrintingP4V3.PrintOptions]
RETURNS [continue: 
BOOL]] = 
BEGIN
FOR i: QueueIndex 
IN QueueIndex 
DO
cont: BOOL;
cont ← p[queue[i].id, ConvertToRequestStatus[queue[i].state], queue[i].printAttributes, queue[i].printOptions];
IF ~cont THEN EXIT;
ENDLOOP;
 
END;
 
GetPrinterStatus: 
PUBLIC 
PROC [h: Handle] 
RETURNS [status: PrinterStatus] ~ {
REPORTS [ServiceUnavailable, SystemError, Undefined]
status ← ConstructPrinterStatus[spoolerBusy, formatterBusy, printerBusy];
RETURN[status];
};
 
FindQueueSlot: 
ENTRY 
PROC 
RETURNS [slot: QueueIndex] = {
ENABLE UNWIND => NULL;
FOR i: QueueIndex 
IN QueueIndex 
DO
IF queue[i].state = empty 
OR queue[i].state = done 
OR queue[i].state = aborted 
THEN {
queue[i].state ← spooling;
RETURN[i];
};
 
ENDLOOP;
 
ERROR SpoolingQueueFull;
};
 
Print: 
PUBLIC 
PROC [h: Handle, master: BulkDataSource, printAttributes: PrintAttributes, printOptions: PrintOptions] 
RETURNS [printRequestID: RequestID] ~ {
REPORTS [Busy, ConnectionError, InsufficientSpoolSpace, InvalidPrintParameters, MasterTooLarge, MediumUnavailable, ServiceUnavailable, SpoolingDisabled, SpoolingQueueFull, SystemError, TooManyClients, TransferError, Undefined]
current: QueueIndex;
MarkQueued: 
ENTRY 
PROC = {
ENABLE UNWIND => NULL;
queue[current].state ← spooled;
queueCount ← queueCount + 1;
BROADCAST queueChanged;
};
 
aborted, sendAbort: BOOL ← FALSE;
out: IO.STREAM ← NIL;
CheckAbort: CrRPC.BulkDataCheckAbortProc ~ {abort ← sendAbort};
{ 
-- for exit
printRequestID ← GetNextID[];
Logger[format: "Print called:", id: printRequestID];
Logger[format: "  printAttributes: %g", v1: IO.rope[PrintingP4V3Aux.ExposePrintAttributes[printAttributes, 2]], id: printRequestID];
Logger[format: "  printOptions: %g", v1: IO.rope[PrintingP4V3Aux.ExposePrintOptions[printOptions, 2]], id: printRequestID];
current ← FindQueueSlot[];
queue[current].id ← printRequestID;
queue[current].state ← spooling;
spoolerBusy ← TRUE;
queue[current].printAttributes ← printAttributes;
queue[current].printOptions ← printOptions;
FS.Delete[queue[current].fileName ! FS.Error => CONTINUE];
out ← FS.StreamOpen[queue[current].fileName, $create];
aborted ← master[h, out, CheckAbort  ! FS.Error => {sendAbort ← TRUE; GOTO Abort}];
IF out # NIL THEN IO.Close[out];
IF aborted THEN GOTO Abort;
spoolerBusy ← FALSE;
MarkQueued[];
EXITS Abort => {
FS.Delete[queue[current].fileName ! FS.Error => CONTINUE];
queue[current].state ← empty;
spoolerBusy ← FALSE;
ERROR TransferError[aborted];
};
 
};  -- for exit
 
};
 
DecomposeAndPrint: 
PROC [] = {
Wait: ENTRY PROC [] ={ENABLE UNWIND => NULL; WAIT queueChanged};
current: QueueIndex ← 0; potential: QueueIndex; found, abort, ok: BOOL ← FALSE;
DO 
-- Forever
WHILE queueCount <= 0 DO Wait[] ENDLOOP;
found ← FALSE;
FOR i: QueueIndex 
IN QueueIndex 
DO
potential ← (current + i) MOD maxQueue;
IF queue[potential].state = spooled THEN {current ← potential; found ← TRUE; EXIT};
ENDLOOP;
 
IF ~found THEN LOOP; -- Excess queueChanged
queue[current].state ← printing;
abort ← FALSE;
formatterBusy ← printerBusy ← TRUE;
ok ← XNSPSPrint.PrintFile[id: queue[current].id, fileName: queue[current].fileName, printAttributes: queue[current].printAttributes, printOptions: queue[current].printOptions ! ABORTED => {abort ← TRUE; CONTINUE}];
queue[current].state ← IF abort OR ~ok THEN aborted ELSE done;
formatterBusy ← printerBusy ← FALSE;
queueCount ← queueCount - 1;
ENDLOOP; -- Forever
 
};
 
 
Init Procs
ConstructPrinterProperties: 
PROC [] 
RETURNS [properties: PrinterProperties] ~ {
properties ← NEW[ PrinterPropertiesObject[3] ];
TRUSTED { properties.body[0] ← [ppmedia[ConstructMedia[]]] };
TRUSTED { properties.body[1] ← [ppstaple[FALSE]] };
TRUSTED { properties.body[2] ← [pptwoSided[FALSE]] };
};
 
ConstructMedia: 
PROC [] 
RETURNS [media: Media] ~ {
media ← NEW[ MediaObject[1] ];
TRUSTED { media.body[0] ← [paper[paper: [knownSize[usLetter]]]] };
TRUSTED { media.body[n] ← [paper[otherSize[width: mm, length: 0]]] }; -- zero is infinity!
};
 
SetUpQueue: 
PROC [] = {
FOR i: QueueIndex 
IN QueueIndex 
DO
queue[i].fileName ← IO.PutFR1["///Spool/File%02g.ip", IO.card[i]];
FS.Delete[queue[i].fileName ! FS.Error => CONTINUE];
ENDLOOP;
 
};
 
Init: 
ENTRY 
PROC ~ {
properties ←  ConstructPrinterProperties[];
SetUpQueue[];
TRUSTED {Process.Detach[FORK DecomposeAndPrint[]]};
};
 
 
Init[];
}...