=
BEGIN
ROPE: TYPE ~ Rope.ROPE;
Request:
TYPE = IPPrinterQueue.Request;
showTimes: BOOL ← TRUE;
diskFileName:
ROPE ←
NIL;
deferSaving: BOOLEAN ← FALSE;
saveDeferred: BOOLEAN ← FALSE;
RequestStatus: TYPE ~ IPPrinterQueue.RequestStatus; -- {notFound, canceled, waiting, printing}
RequestQueue: TYPE ~ REF RequestQueueRep;
RequestQueueRep:
TYPE ~
RECORD [
next: RequestQueue ← NIL,
number: CARDINAL ← 0,
request: IPPrinterQueue.Request ← [NIL, NIL, NIL, NIL, NIL, 0],
status: RequestStatus ← notFound];
queueHead: RequestQueue ← NIL;
reqNumber: CARDINAL ← 0;
Save:
PROC = {
IF deferSaving THEN {saveDeferred ← TRUE; RETURN};
IF diskFileName = NIL THEN RETURN;
DumpToDisk[diskFileName, queueHead];
saveDeferred ← FALSE;
};
DumpToDisk:
PROC [diskFileName:
ROPE, queueHead: RequestQueue] =
BEGIN
s: IO.STREAM ← FS.StreamOpen[diskFileName, create ! FS.Error => GOTO Out];
DO
ENABLE FS.Error => EXIT;
IF queueHead = NIL THEN EXIT;
IO.PutF1[s, "%g ", IO.int[queueHead.number]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.fileName]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.requestTime]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.requestor]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.requestorPassword]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.separator]];
IO.PutF1[s, "%g ", IO.card[queueHead.request.copies]];
SELECT queueHead.status
FROM
notFound => IO.PutRope[s, "notFound\n"];
canceled => IO.PutRope[s, "canceled\n"];
waiting => IO.PutRope[s, "waiting\n"];
printing => IO.PutRope[s, "printing\n"];
ENDCASE => ERROR;
queueHead ← queueHead.next;
ENDLOOP;
IO.Close[s ! FS.Error => GOTO Out];
EXITS Out => RETURN;
END;
Restore:
PROC = {
IF diskFileName = NIL THEN RETURN;
queueHead ← LoadFromDisk[diskFileName];
};
LoadFromDisk:
PROC [diskFileName:
ROPE]
RETURNS [queueHead: RequestQueue ←
NIL] =
BEGIN
s: IO.STREAM ← FS.StreamOpen[diskFileName, create ! FS.Error => GOTO Out];
r: CARDINAL; rope: ROPE;
tail: RequestQueue ← NIL;
DO
ENABLE FS.Error => EXIT;
r ← IO.GetCard[s ! IO.EndOfStream => EXIT];
IF tail =
NIL
THEN queueHead ← tail ←
NEW[RequestQueueRep]
ELSE {tail.next ← NEW[RequestQueueRep]; tail ← tail.next};
tail.number ← r;
tail.request.fileName ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.requestTime ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.requestor ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.requestorPassword ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.separator ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.copies ← IO.GetCard[s ! IO.EndOfStream => GOTO Out];
rope ← IO.GetTokenRope[s ! IO.EndOfStream => GOTO Out].token;
tail.status ←
SELECT
TRUE
FROM
Rope.Equal[rope, "notFound", FALSE] => notFound,
Rope.Equal[rope, "canceled", FALSE] => canceled,
Rope.Equal[rope, "waiting", FALSE] => waiting,
Rope.Equal[rope, "printing", FALSE] => printing,
ENDCASE => ERROR;
ENDLOOP;
reqNumber ← r;
IO.Close[s ! FS.Error => GOTO Out];
EXITS Out => RETURN [NIL];
END;
InternalReset:
INTERNAL
PROC = {
queueHead ← NIL;
};
Reset:
PUBLIC ENTRY
PROC = {InternalReset[]; Save[]};
requestLimit:
CARDINAL ← 1000;
Requests are numbered modulo this number.
QueueRequest:
PUBLIC
ENTRY
PROC [request: Request]
RETURNS [requestNumber:
INT] = {
ENABLE UNWIND => NULL;
tail: RequestQueue; msg: ROPE;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
IF tail =
NIL
THEN tail ← queueHead ← NEW[RequestQueueRep]
ELSE {
DO
IF tail.next = NIL THEN EXIT;
tail ← tail.next;
ENDLOOP;
tail.next ← NEW[RequestQueueRep]; tail ← tail.next;
};
requestNumber ← reqNumber ← tail.number ← (reqNumber + 1) MOD requestLimit;
tail.request ← request;
tail.status ← waiting;
msg ← request.fileName;
IF request.copies # 1
THEN
msg ← IO.PutFR["%g (%g copies)", IO.rope[msg], IO.card[request.copies]];
InternalLogMessage[msg, requestNumber, request.requestor];
Save[];
NOTIFY newRequest;
};
LogMessage:
PUBLIC
ENTRY
PROC [message:
ROPE, requestNumber:
INT ← -1, userName:
ROPE ←
NIL, printCR:
BOOL ←
TRUE] = {
InternalLogMessage[message, requestNumber, userName, printCR];
};
reprintCopies: CARDINAL ← 0;
reprintCancelled:
BOOLEAN ←
FALSE;
Reprint:
PUBLIC
ENTRY
PROC [copies:
CARDINAL] = {
reprintCancelled ← FALSE;
reprintCopies ← reprintCopies + copies;
NOTIFY newRequest;
};
CancelReprint:
PUBLIC
ENTRY
PROC = {
reprintCancelled ← TRUE;
reprintCopies ← 0;
};
ReprintCancelled:
PUBLIC
PROC
RETURNS [
BOOLEAN] = {
RETURN [reprintCancelled]};
printingSuspended:
BOOLEAN ←
FALSE;
GetSuspended: PUBLIC ENTRY PROC RETURNS [BOOLEAN] = {RETURN [printingSuspended]};
SetSuspended:
PUBLIC
ENTRY
PROC [suspended:
BOOLEAN]
RETURNS [old:
BOOLEAN] = {
old ← printingSuspended;
printingSuspended ← suspended;
NOTIFY newRequest;
};
abortCurrent:
REF
BOOL ←
NEW[
BOOL ←
FALSE];
FindRequest:
PROC [requestNumber:
CARDINAL]
RETURNS [request: Request, status: RequestStatus] = {
ENABLE UNWIND => NULL;
tail: RequestQueue;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
IF tail = NIL THEN EXIT;
IF tail.number = requestNumber THEN RETURN [tail.request, tail.status];
tail ← tail.next;
ENDLOOP;
RETURN [[NIL, NIL, NIL, NIL, NIL, 0], notFound];
};
GetFirstRequest:
ENTRY
PROC []
RETURNS [requestNumber:
INT, copies:
CARDINAL, request: Request] = {
ENABLE UNWIND => NULL;
tail: RequestQueue;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
WHILE printingSuspended OR (queueHead = NIL AND reprintCopies = 0) DO WAIT newRequest ENDLOOP;
abortCurrent^ ← FALSE;
reprintCancelled ← FALSE;
IF reprintCopies # 0
THEN {
requestNumber ← -1;
copies ← reprintCopies;
reprintCopies ← 0;
RETURN[requestNumber, copies, [NIL, NIL, NIL, NIL, NIL, copies]];
};
tail ← queueHead;
IF tail = NIL THEN ERROR;
RETURN [tail.number, tail.request.copies, tail.request];
};
RemoveRequest:
ENTRY
PROC [requestNumber:
CARDINAL] = {
ENABLE UNWIND => NULL;
tail, oldTail: RequestQueue;
tail ← queueHead;
oldTail ← queueHead;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
IF tail = NIL THEN EXIT;
IF tail.number = requestNumber
THEN {
IF tail.status = printing THEN abortCurrent^ ← TRUE;
IF tail = queueHead THEN queueHead ← tail.next ELSE oldTail.next ← tail.next;
EXIT;
};
oldTail ← tail;
tail ← tail.next;
ENDLOOP;
Save[];
};
SetStatus:
ENTRY
PROC [requestNumber:
CARDINAL, status: RequestStatus]
RETURNS [ok:
BOOL] =
BEGIN
tail: RequestQueue;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
IF tail = NIL THEN EXIT;
IF tail.number = requestNumber THEN {tail.status ← status; RETURN[TRUE]};
tail ← tail.next;
ENDLOOP;
RETURN [FALSE];
END;
DeferSaving:
PUBLIC
ENTRY
PROC = {
ENABLE UNWIND => NULL;
deferSaving ← TRUE;
};
DoDeferredSave:
PUBLIC
ENTRY
PROC = {
ENABLE UNWIND => NULL;
deferSaving ← FALSE;
IF saveDeferred THEN Save[];
};
DoRequest:
PUBLIC
PROC [action:
PROC [request: Request, requestNumber:
CARDINAL, abort:
REF
BOOLEAN]] = {
request: Request;
requestNumber: INT;
copies: CARDINAL;
aborted: BOOL ← FALSE;
[requestNumber, copies, request] ← GetFirstRequest[];
IF requestNumber >= 0
THEN {
LogMessage["Started.", requestNumber, request.requestor];
[]← SetStatus[requestNumber, printing];
action[request, requestNumber, abortCurrent ! ABORTED => {aborted ← TRUE; CONTINUE}];
IF aborted THEN LogMessage["Aborted.", requestNumber, request.requestor]
ELSE LogMessage["Completed.", requestNumber, request.requestor];
RemoveRequest[requestNumber];
};
};
CancelRequest:
PUBLIC
PROC [requestNumber:
CARDINAL]
RETURNS [ok:
BOOLEAN] = {
RemoveRequest[requestNumber];
RETURN [TRUE];
};
CheckRequest:
PUBLIC
PROC [requestNumber:
CARDINAL,
action:
PROC [request: Request, status: RequestStatus]] = {
status: RequestStatus ← notFound;
request: Request;
[request: request, status: status] ← FindRequest[requestNumber];
action[[request.fileName, request.requestTime, request.requestor, request.requestorPassword, request.separator, request.copies], status];
};
CountRequests:
PUBLIC
ENTRY
PROC
RETURNS [requestCount:
NAT] = {
tail: RequestQueue ← queueHead;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
requestCount ← 0;
DO
IF tail = NIL THEN EXIT;
requestCount ← requestCount + 1;
tail ← tail.next;
ENDLOOP;
};
EnumerateRequests:
PUBLIC
ENTRY
PROC [action:
PROC [requestNumber:
CARDINAL, request: Request, status: RequestStatus]
RETURNS [continue:
BOOLEAN]] = {
tail: RequestQueue ← NIL;
requestNumber: CARDINAL;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
continue: BOOLEAN ← TRUE;
myAction:
PROC [request: Request, status: RequestStatus] = {
IF status = printing OR status = waiting THEN continue ← action[requestNumber, request, status];
};
IF tail = NIL THEN EXIT;
CheckRequest[(requestNumber ← tail.number), myAction];
IF NOT continue THEN EXIT;
tail ← tail.next;
ENDLOOP;
};
RegisterDisk:
PUBLIC
ENTRY
PROC [file:
ROPE ←
NIL] = {diskFileName ← file};
usingDisk:
PROCESS ←
NIL;
Records the process using the disk under the monitor, to avoid deadlocks in case of disk errors.
MsgList: TYPE ~ REF MsgListRep;
MsgListRep:
TYPE ~
RECORD [
next: MsgList ← NIL,
msg: ROPE ← NIL];
msgHead: MsgList ← NIL;
msgTail: MsgList ← NIL;
msgCnt: CARDINAL ← 0;
InternalLogMessage:
INTERNAL
PROC [message:
ROPE, requestNumber:
INT ← -1, userName:
ROPE ←
NIL, printCR:
BOOL ←
TRUE] = {
scratch: REF TEXT ~ RefText.ObtainScratch[100];
text: REF TEXT ← scratch;
msg: ROPE ← NIL;
IF msgHead =
NIL
THEN msgHead ← msgTail ←
NEW[MsgListRep]
ELSE {msgTail.next ← NEW[MsgListRep]; msgTail ← msgTail.next};
IF showTimes
THEN {
text ← TimeToRefText[BasicTime.Now[], text];
};
text ← RefText.AppendChar[text, ' ];
IF requestNumber >= 0
THEN {
text ← RefText.AppendChar[text, 'R];
text ← RefText.AppendChar[text, '#];
text ← Convert.AppendInt[to: text, from: requestNumber];
text ← RefText.AppendChar[text, ' ];
};
text ← RefText.AppendChar[text, '\t];
text ← RefText.AppendRope[text, userName];
text ← RefText.AppendChar[text, '\t];
text ← RefText.AppendRope[text, message];
IF printCR
OR showTimes
THEN {
text ← RefText.AppendChar[text, '\n];
};
msg ← Rope.FromRefText[text];
msgTail.msg ← msg; msgCnt ← msgCnt + 1;
IF serverStream # NIL THEN {IO.PutRope[serverStream, msg]; IO.Flush[serverStream]};
IF msgCnt > 150
THEN
BEGIN
IF msgHead.next = NIL THEN {msgCnt ← 1; RETURN};
msgCnt ← msgCnt - 1;
msgHead ← msgHead.next;
END;
RefText.ReleaseScratch[scratch];
};
monthName:
ARRAY BasicTime.MonthOfYear
OF Rope.
ROPE ~ ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"];
TimeToRefText:
PROC [time: BasicTime.
GMT ← BasicTime.nullGMT, text:
REF
TEXT]
RETURNS [t:
REF
TEXT] =
BEGIN
unpacked: BasicTime.Unpacked ← BasicTime.Unpack[(IF time = BasicTime.nullGMT THEN BasicTime.Now[] ELSE time)];
stream: IO.STREAM ← IO.TOS[text];
zoneChar: CHAR ← ' ;
IO.PutF[stream, "%2g-%g-%02g ", IO.int[unpacked.day], IO.rope[monthName[unpacked.month]], IO.int[unpacked.year MOD 100]];
IO.PutF[stream, "%2g:%02g:%02g", IO.int[unpacked.hour], IO.int[unpacked.minute], IO.int[unpacked.second]];
t ← IO.TextFromTOS[stream];
END;
loggingMessages: BOOLEAN ← FALSE;
serverStream: IO.STREAM ← NIL;
RegisterTTY:
PUBLIC
ENTRY
PROC [stream:
IO.
STREAM] = {serverStream ← stream};
SetLogState:
PUBLIC
ENTRY
PROC [logging:
BOOLEAN]
RETURNS [old:
BOOLEAN] = {
old ← loggingMessages;
loggingMessages ← logging;
};
EnumerateMessages:
PUBLIC
PROC [action:
PROC[message:
ROPE]
RETURNS [continue:
BOOLEAN]] =
BEGIN
myTail: MsgList ← msgHead;
DO
IF myTail = NIL THEN RETURN;
IF NOT action[myTail.msg] THEN RETURN;
myTail ← myTail.next;
ENDLOOP;
END;