PDQueueImpl:
MONITOR
IMPORTS Time, Checksum, Inline, PDInterpSysCalls, String, Process
EXPORTS PDQueue
= BEGIN
Request:
TYPE = PDQueue.Request;
readProc:
PROC [address:
LONG
POINTER, nwords:
CARDINAL] ←
NIL;
writeProc:
PROC [address:
LONG
POINTER, nwords:
CARDINAL] ←
NIL;
maxStateWords:
CARDINAL ← 16*1024;
deferSaving: BOOLEAN ← FALSE;
saveDeferred: BOOLEAN ← FALSE;
state: LONG POINTER TO StringSeqRep ← NIL;
messages:
LONG
POINTER
TO StringSeqRep ←
NIL;
StringSeqRep:
TYPE =
MACHINE
DEPENDENT
RECORD [
nStrings: CARDINAL,
end: CARDINAL,
mark: CARDINAL,
nMarked: CARDINAL,
nDiscarded: CARDINAL ← 0,
check: CARDINAL,
nWords: CARDINAL,
stringSpace: SEQUENCE COMPUTED CARDINAL OF WORD
];
StringRep:
TYPE =
RECORD [
nChars: CARDINAL,
char: PACKED ARRAY [0..0) OF CHAR
];
Validate:
INTERNAL
PROC [s:
LONG
POINTER
TO StringSeqRep]
RETURNS [ok:
BOOLEAN] = {
nStrings: CARDINAL ← 0;
nMarked: CARDINAL ← 0;
offset: CARDINAL ← 0;
IF s.check # CalculateCheck[s] THEN RETURN [FALSE];
IF s.mark > s.end THEN RETURN [FALSE];
WHILE offset < s.end
DO
words: CARDINAL;
nStrings ← nStrings + 1;
IF offset > s.mark AND nMarked = 0 THEN RETURN [FALSE];
IF offset >= s.mark THEN nMarked ← nMarked + 1;
words ← (s[offset]+Environment.bytesPerWord-1)/Environment.bytesPerWord+1;
offset ← offset + words;
ENDLOOP;
IF offset # s.end THEN RETURN [FALSE];
IF nStrings # s.nStrings THEN RETURN [FALSE];
IF nMarked # s.nMarked THEN RETURN [FALSE];
RETURN [TRUE]
};
DiscardMessages:
INTERNAL
PROC [maxWords:
CARDINAL] = {
WHILE messages.end > maxWords DO Discard[messages, 1] ENDLOOP;
};
Discard:
INTERNAL
PROC [s:
LONG
POINTER
TO StringSeqRep, n:
CARDINAL] = {
newStart: CARDINAL ← StringOffset[s, n];
Inline.LongCOPY[from: @(s[newStart]), nwords: s.end-newStart, to: @(s[0])];
s.nStrings ← s.nStrings - n;
s.end ← s.mark ← s.end-newStart;
s.nDiscarded ← s.nDiscarded + n;
};
StringOffset:
INTERNAL
PROC [s:
LONG
POINTER
TO StringSeqRep, n:
CARDINAL]
RETURNS [offset:
CARDINAL] = {
offset ← 0;
IF n = 0 THEN RETURN;
IF n > s.nStrings THEN ERROR;
FOR i:
NAT
IN [0..n)
DO
offset ← NextStringOffset[s, offset];
ENDLOOP;
};
NextStringOffset:
INTERNAL
PROC [s:
LONG
POINTER
TO StringSeqRep, offset:
CARDINAL]
RETURNS [
CARDINAL] =
INLINE {
IF offset >= s.end THEN ERROR
ELSE {
words: CARDINAL ← (s[offset]+Environment.bytesPerWord-1)/Environment.bytesPerWord+1;
IF words > s.end-offset THEN ERROR;
offset ← offset + words;
RETURN [offset];
};
};
Append:
INTERNAL
PROC [s:
LONG
POINTER
TO StringSeqRep, string:
LONG
STRING] = {
body: LONG POINTER TO StringRep;
chars: CARDINAL ← IF string = NIL THEN 0 ELSE string.length;
words: CARDINAL ← (string.length+Environment.bytesPerWord-1)/Environment.bytesPerWord+1;
IF s.mark # s.end THEN ERROR;
IF s.end+words > s.nWords THEN ERROR;
body ← LOOPHOLE[@(s[s.end])];
body.nChars ← chars;
FOR i:
NAT
IN [0..chars)
DO
body.char[i] ← string[i];
ENDLOOP;
IF chars MOD 2 = 1 THEN body.char[chars] ← '\000;
s.mark ← s.end ← s.end+words;
s.nStrings ← s.nStrings + 1;
};
AppendToString:
INTERNAL
PROC [string:
LONG
STRING, s:
LONG
POINTER
TO StringSeqRep, offset:
CARDINAL] = {
body: LONG POINTER TO StringRep;
IF offset >= s.end THEN ERROR;
IF offset+(s[offset]+Environment.bytesPerWord-1)/Environment.bytesPerWord+1 > s.end THEN ERROR;
body ← LOOPHOLE[@(s[offset])];
FOR i:
NAT
IN [0..body.nChars)
DO
string[string.length] ← body.char[i];
string.length ← string.length + 1;
ENDLOOP;
};
MoveToString:
INTERNAL
PROC [string:
LONG
STRING, s:
LONG
POINTER
TO StringSeqRep, offset:
CARDINAL] = {
string.length ← 0;
AppendToString[string, s, offset];
};
GetNumberDiscardedMessages:
ENTRY
PROC
RETURNS [
CARDINAL] = {
IF messages = NIL THEN Restore[];
RETURN [messages.nDiscarded];
};
GetString:
ENTRY
PROC [string:
LONG
STRING, s:
LONG
POINTER
TO StringSeqRep, n:
CARDINAL]
RETURNS [ok:
BOOLEAN] = {
string.length ← 0;
IF n-s.nDiscarded > 32767
THEN
RETURN [
TRUE];
-- funny condition due to unsigned arithmetic . . . nDiscarded may wrap
IF n-s.nDiscarded >= s.nStrings THEN RETURN [FALSE];
AppendToString[string, s, StringOffset[s, n-s.nDiscarded]];
RETURN [TRUE];
};
MoveMarkedStrings:
INTERNAL
PROC [from, to:
LONG
POINTER
TO StringSeqRep] = {
nwords: CARDINAL ← from.end-from.mark;
IF to.nWords < to.mark+nwords THEN ERROR;
to.nStrings ← to.nStrings - to.nMarked;
to.nMarked ← 0;
to.end ← to.mark;
Inline.LongCOPY[from: @(from[from.mark]), nwords: nwords, to: @(to[to.mark])];
to.nStrings ← to.nStrings + from.nMarked;
to.nMarked ← from.nMarked;
to.end ← to.end + nwords;
from.nStrings ← from.nStrings-from.nMarked;
from.nMarked ← 0;
from.end ← from.mark;
};
CalculateCheck:
INTERNAL
PROC [s:
LONG
POINTER
TO StringSeqRep]
RETURNS [
CARDINAL] = {
cs: CARDINAL ← Checksum.ComputeChecksum[nWords: 5, p: s];
cs ← Checksum.ComputeChecksum[cs: cs, nWords: 1, p: s+6];
RETURN [cs];
};
Save:
INTERNAL
PROC = {
IF deferSaving THEN {saveDeferred ← TRUE; RETURN};
IF state = NIL THEN ERROR;
DiscardMessages[state.nWords-state.end];
messages.nMarked ← messages.nStrings;
messages.mark ← 0;
MoveMarkedStrings[from: messages, to: state];
state.check ← CalculateCheck[state];
IF NOT Validate[state] THEN ERROR;
IF writeProc #
NIL
THEN {
usingDisk ← Process.GetCurrent[];
writeProc[state, SIZE[StringSeqRep[state.end]]];
usingDisk ← NIL;
};
MoveMarkedStrings[from: state, to: messages];
messages.nMarked ← 0;
messages.mark ← messages.end;
saveDeferred ← FALSE;
};
Restore:
INTERNAL
PROC = {
IF state # NIL THEN ERROR;
IF readProc #
NIL
THEN {
temp: LONG POINTER TO StringSeqRep ← PDInterpSysCalls.AllocateSpace[256];
usingDisk ← Process.GetCurrent[];
readProc[temp, 256];
usingDisk ← NIL;
IF temp.check = CalculateCheck[temp]
THEN {
words: CARDINAL ← SIZE[StringSeqRep[temp.end]];
wordsAllocated: CARDINAL ← MAX[maxStateWords, words+255]/256*256;
IF wordsAllocated <= maxStateWords
THEN {
state ← PDInterpSysCalls.AllocateSpace[wordsAllocated];
usingDisk ← Process.GetCurrent[];
readProc[state, words];
usingDisk ← NIL;
state.nWords ← wordsAllocated-SIZE[StringSeqRep[0]];
IF Validate[state]
THEN {
AllocMessages[];
MoveMarkedStrings[from: state, to: messages];
messages.nMarked ← 0;
messages.mark ← messages.end;
firstUnprintedMessage ← messages.nDiscarded+messages.nStrings-24;
-- it should be ok if this wraps around.
}
ELSE {
PDInterpSysCalls.FreeSpace[state];
state ← NIL
};
};
};
PDInterpSysCalls.FreeSpace[temp];
temp ← NIL;
};
IF state = NIL THEN InternalReset[];
};
AllocMessages:
INTERNAL
PROC = {
wordsAllocated: CARDINAL ← SIZE[StringSeqRep[state.nWords]];
IF messages # NIL THEN {PDInterpSysCalls.FreeSpace[messages]; messages ← NIL};
messages ← PDInterpSysCalls.AllocateSpace[wordsAllocated];
messages.nWords ← wordsAllocated-SIZE[StringSeqRep[0]];
messages.nStrings ← messages.end ← messages.mark ← messages.nMarked ← messages.nDiscarded ← 0;
};
InternalReset:
INTERNAL
PROC = {
IF state # NIL THEN {PDInterpSysCalls.FreeSpace[state]; state ← NIL};
state ← PDInterpSysCalls.AllocateSpace[maxStateWords];
state.nWords ← maxStateWords-SIZE[StringSeqRep[0]];
state.nStrings ← state.end ← state.mark ← state.nMarked ← state.nDiscarded ← 0;
AllocMessages[];
};
Reset:
PUBLIC ENTRY
PROC = {InternalReset[]; Save[]};
stringsPerRequest:
CARDINAL = 6;
requestLimit:
CARDINAL ← 1000;
Requests are numbered modulo this number.
ReqNumberFromStringNumber:
PROC [stringNumber:
CARDINAL]
RETURNS [requestNumber:
CARDINAL] = {
requestNumber ← (stringNumber/stringsPerRequest) MOD requestLimit;
};
DecodeCopies:
PROC [tag:
LONG
STRING]
RETURNS [copies:
CARDINAL] = {
copies ← ORD[tag[2]]*256+ORD[tag[3]];
};
QueueRequest:
PUBLIC
ENTRY
PROC [request: Request]
RETURNS [requestNumber:
INT] = {
ENABLE UNWIND => NULL;
tag: LONG STRING ← [4];
wordsNeeded: INT ← 2*(INT[4]+request.fileName.length+request.requestTime.length+request.requestor.length+request.requestorPassword.length+request.separator.length) + stringsPerRequest*(SIZE[StringRep]+1);
IF state = NIL THEN Restore[];
IF state.nStrings >= stringsPerRequest*requestLimit THEN RETURN [-1];
IF state.end+wordsNeeded+10 >= state.nWords THEN RETURN [-1];
requestNumber ← ReqNumberFromStringNumber[state.nStrings+state.nDiscarded];
String.AppendString[tag, " "];
String.AppendChar[tag, VAL[Inline.HighByte[CARDINAL[request.copies]]]];
String.AppendChar[tag, VAL[Inline.LowByte[CARDINAL[request.copies]]]];
Append[state, tag];
Append[state, request.fileName];
Append[state, request.requestTime];
Append[state, request.requestor];
Append[state, request.requestorPassword];
Append[state, request.separator];
IF stringsPerRequest # 6 THEN ERROR;
BEGIN
msg: LONG STRING ← [250];
String.AppendString[msg, request.fileName];
IF request.copies#1
THEN {
String.AppendString[msg, " ("];
String.AppendDecimal[msg, request.copies];
String.AppendString[msg, " copies)"];
};
InternalLogMessage[msg, requestNumber, request.requestor];
END;
Save[];
NOTIFY newRequest;
};
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;
};
FirstRequest:
ENTRY
PROC
RETURNS [requestNumber:
CARDINAL] = {
ENABLE UNWIND => NULL;
IF state = NIL THEN Restore[];
requestNumber ← ReqNumberFromStringNumber[state.nDiscarded];
};
FindRequest:
ENTRY
PROC [requestNumber:
CARDINAL, tag:
LONG
STRING, request: Request] = {
ENABLE UNWIND => NULL;
stringNumber: CARDINAL ← requestNumber*stringsPerRequest;
offset: CARDINAL ← 0;
IF state = NIL THEN Restore[];
WHILE stringNumber < state.nDiscarded DO stringNumber ← stringNumber + requestLimit*stringsPerRequest ENDLOOP;
IF stringNumber-state.nDiscarded >= state.nStrings THEN RETURN;
offset ← StringOffset[state, stringNumber-state.nDiscarded];
MoveToString[tag, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.fileName, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.requestTime, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.requestor, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.requestorPassword, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.separator, state, offset];
IF stringsPerRequest # 6 THEN ERROR;
};
GetRequest:
ENTRY
PROC [request: Request]
RETURNS [requestNumber:
INT, copies:
CARDINAL] = {
ENABLE UNWIND => NULL;
tag: LONG STRING ← [4];
IF state = NIL THEN Restore[];
DO
offset: CARDINAL ← 0;
WHILE printingSuspended OR (state.nStrings = 0 AND reprintCopies = 0) DO WAIT newRequest ENDLOOP;
abortCurrent ← FALSE;
reprintCancelled ← FALSE;
IF reprintCopies # 0
THEN {
requestNumber ← -1;
copies ← reprintCopies;
reprintCopies ← 0;
RETURN;
};
requestNumber ← ReqNumberFromStringNumber[state.nDiscarded];
MoveToString[tag, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.fileName, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.requestTime, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.requestor, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.requestorPassword, state, offset]; offset ← NextStringOffset[state, offset];
MoveToString[request.separator, state, offset];
copies ← DecodeCopies[tag];
IF stringsPerRequest # 6 THEN ERROR;
IF tag[0] = ' THEN EXIT;
Discard[state, stringsPerRequest];
WHILE state.nDiscarded >= stringsPerRequest*requestLimit
DO
state.nDiscarded ← state.nDiscarded - stringsPerRequest*requestLimit;
ENDLOOP;
ENDLOOP;
};
RemoveRequest:
ENTRY
PROC [requestNumber:
CARDINAL] = {
ENABLE UNWIND => NULL;
IF state = NIL THEN Restore[];
IF requestNumber # ReqNumberFromStringNumber[state.nDiscarded] THEN ERROR;
Discard[state, stringsPerRequest];
WHILE state.nDiscarded >= stringsPerRequest*requestLimit
DO
state.nDiscarded ← state.nDiscarded - stringsPerRequest*requestLimit;
ENDLOOP;
Save[];
};
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:
LONG
POINTER
TO
BOOLEAN]] = {
fileName: LONG STRING ← [80];
requestTime: LONG STRING ← [20];
requestor: LONG STRING ← [80];
requestorPassword: LONG STRING ← [80];
separator: LONG STRING ← [80];
requestNumber: INT;
body: LONG POINTER TO StringRep;
copies: CARDINAL;
aborted: BOOLEAN ← FALSE;
[requestNumber, copies] ← GetRequest[[fileName, requestTime, requestor, requestorPassword, separator, 0]];
IF requestNumber < 0
THEN {
LogMessage["Reprint."];
action[[NIL, NIL, NIL, NIL, NIL, copies], NAT.LAST, @abortCurrent ! ABORTED => {aborted ← TRUE; CONTINUE}];
LogMessage["Reprint completed."];
}
ELSE {
body ← LOOPHOLE[@(state[0])];
IF body.nChars # 4 THEN ERROR;
body.char[1] ← 'P;
LogMessage["Started.", requestNumber, requestor];
action[[fileName, requestTime, requestor, requestorPassword, separator, copies], requestNumber, @abortCurrent ! ABORTED => {aborted ← TRUE; CONTINUE}];
IF aborted THEN LogMessage["Aborted.", requestNumber, requestor]
ELSE LogMessage["Completed.", requestNumber, requestor];
RemoveRequest[requestNumber];
};
};
CancelRequest:
PUBLIC
ENTRY
PROC [requestNumber:
CARDINAL]
RETURNS [ok:
BOOLEAN] = {
stringNumber: CARDINAL ← requestNumber*stringsPerRequest;
body: LONG POINTER TO StringRep;
IF state = NIL THEN Restore[];
WHILE stringNumber < state.nDiscarded
DO
stringNumber ← stringNumber+requestLimit*stringsPerRequest
ENDLOOP;
IF stringNumber-state.nDiscarded >= state.nStrings THEN RETURN [FALSE];
body ← LOOPHOLE[@(state[StringOffset[state, stringNumber-state.nDiscarded]])];
IF body.nChars # 4 THEN ERROR;
IF body.char[0] # ' THEN RETURN [FALSE];
body.char[0] ← 'X;
IF stringNumber = state.nDiscarded THEN abortCurrent ← TRUE;
Save[];
RETURN [TRUE];
};
RequestStatus: TYPE = PDQueue.RequestStatus;
CheckRequest:
PUBLIC
PROC [requestNumber:
CARDINAL, action:
PROC [request: Request, status: RequestStatus]] = {
tag: LONG STRING ← [4];
status: RequestStatus;
fileName: LONG STRING ← [80];
requestTime: LONG STRING ← [20];
requestor: LONG STRING ← [80];
requestorPassword: LONG STRING ← [80];
separator: LONG STRING ← [80];
tag.length ← 0;
FindRequest[requestNumber, tag, [fileName, requestTime, requestor, requestorPassword, separator, 0]];
IF tag.length = 0 THEN status ← notFound
ELSE IF tag[0] = 'X THEN status ← canceled
ELSE IF tag[1] = 'P THEN status ← printing
ELSE status ← waiting;
action[[fileName, requestTime, requestor, requestorPassword, separator, DecodeCopies[tag]], status];
};
CountRequests:
PUBLIC
ENTRY
PROC
RETURNS [requestCount:
NAT] = {
IF state = NIL THEN Restore[];
requestCount ← state.nStrings/stringsPerRequest;
};
EnumerateRequests:
PUBLIC
PROC [action:
PROC [requestNumber:
CARDINAL, request: Request, status: RequestStatus]
RETURNS [continue:
BOOLEAN]] = {
firstRequest: NAT ← FirstRequest[];
nRequests: NAT ← CountRequests[];
requestNumber: NAT ← firstRequest MOD requestLimit;
THROUGH [0..nRequests)
DO
continue: BOOLEAN ← TRUE;
myAction:
PROC [request: Request, status: RequestStatus] = {
IF status = printing OR status = waiting THEN continue ← action[requestNumber, request, status];
};
CheckRequest[requestNumber MOD requestLimit, myAction];
IF NOT continue THEN EXIT;
requestNumber ← (requestNumber + 1) MOD requestLimit;
ENDLOOP;
};
RegisterDisk:
PUBLIC
ENTRY
PROC [read, write:
PROC [address:
LONG
POINTER, nwords:
CARDINAL], maxWords:
CARDINAL] = {
IF state #
NIL
THEN {
PDInterpSysCalls.FreeSpace[state];
state ← NIL;
};
readProc ← read;
writeProc ← write;
maxStateWords ← maxWords;
};
usingDisk:
PROCESS ←
NIL;
Records the process using the disk under the monitor, to avoid deadlocks in case of disk errors.
LogMessage:
PUBLIC
PROC [message:
LONG
STRING, requestNumber:
INT ← -1, userName:
LONG
STRING ←
NIL] = {
IF usingDisk = Process.GetCurrent[]
THEN {
IF writeLineProc =
NIL
THEN
ERROR;
Remote debug if it ever gets this bad.
writeLineProc["Help!" ! ABORTED => CONTINUE];
writeLineProc[message ! ABORTED => CONTINUE];
}
ELSE LogMessageEntry[message, requestNumber, userName];
};
LogMessageEntry:
ENTRY
PROC [message:
LONG
STRING, requestNumber:
INT ← -1, userName:
LONG
STRING ←
NIL] = {
InternalLogMessage[message, requestNumber, userName];
};
InternalLogMessage:
INTERNAL
PROC [message:
LONG
STRING, requestNumber:
INT ← -1, userName:
LONG
STRING ←
NIL] = {
msg: LONG STRING ← [250];
Time.AppendCurrent[msg];
msg[msg.length] ← ' ; msg.length ← msg.length + 1;
IF requestNumber >= 0
THEN {
String.AppendChar[msg, 'R];
FOR i:
CARDINAL ← requestLimit/10, i/10
WHILE i#0
DO
String.AppendDecimal[msg, (requestNumber/i) MOD 10];
ENDLOOP;
String.AppendChar[msg, ' ];
};
String.AppendString[msg, message];
IF userName#
NIL
THEN {
String.AppendString[msg, " ("];
String.AppendString[msg, userName];
String.AppendChar[msg, ')];
};
IF state = NIL THEN Restore[];
DiscardMessages[messages.nWords-SIZE[StringBody[msg.length]]];
Append[messages, msg];
TryToPrintMessages[];
};
loggingMessages: BOOLEAN ← FALSE;
firstUnprintedMessage: CARDINAL ← 0;
writeLineProc:
PROC[
LONG
STRING] ←
NIL;
RegisterTTY:
PUBLIC
ENTRY
PROC [writeLine:
PROC[
LONG
STRING]] = {
writeLineProc ← writeLine;
TryToPrintMessages[];
};
TryToPrintMessages:
INTERNAL
PROC = {
msg: LONG STRING ← [250];
GetMsg:
INTERNAL
PROC [n:
CARDINAL]
RETURNS [ok:
BOOLEAN] = {
msg.length ← 0;
IF n-messages.nDiscarded > 32767
THEN
RETURN [
TRUE];
-- funny condition due to unsigned arithmetic . . . nDiscarded may wrap
IF n-messages.nDiscarded >= messages.nStrings THEN RETURN [FALSE];
AppendToString[msg, messages, StringOffset[messages, n-messages.nDiscarded]];
RETURN [TRUE];
};
IF NOT loggingMessages OR writeLineProc = NIL THEN RETURN;
FOR i:
CARDINAL ← firstUnprintedMessage, i+1
UNTIL
NOT GetMsg[i]
DO
writeLineProc[msg ! ABORTED => CONTINUE];
firstUnprintedMessage ← i+1;
ENDLOOP;
};
SetLogState:
PUBLIC
ENTRY
PROC [logging:
BOOLEAN]
RETURNS [old:
BOOLEAN] = {
old ← loggingMessages;
loggingMessages ← logging;
IF messages # NIL THEN TryToPrintMessages[];
};
EnumerateMessages:
PUBLIC
PROC [action:
PROC[message:
LONG
STRING]
RETURNS [continue:
BOOLEAN]] = {
msg: LONG STRING ← [250];
FOR i:
CARDINAL ← GetNumberDiscardedMessages[], i+1
UNTIL
NOT GetString[msg, messages, i]
DO
IF NOT action[msg].continue THEN EXIT;
ENDLOOP;
};
END.