BEGIN
ENABLE Stream.EndOfStream, Stream.TimeOut =>
GOTO Quit;
command: LONG STRING ← [80];
password: LONG STRING ← [80];
account: LONG STRING ← [80];
quitting: BOOLEAN ← FALSE;
accessAllowed: BOOLEAN ← TRUE;
echo: BOOLEAN ← TRUE;
flushed: BOOLEAN ← FALSE;
commandCode: CommandCode;
lastRequest: INT ← -1;
PutChar:
PROC [char:
CHAR] = {
Stream.PutChar[stream, char];
flushed ← FALSE;
};
PutString:
PROC [string:
LONG
STRING] = {
Stream.PutString[stream, string];
flushed ← FALSE;
};
PutLine:
PROC = {
PutChar['\n];
PutChar['\012];
};
PutOK:
PROC = {
PutLine[];
PutChar['o];
PutChar['k];
PutLine[];
};
PutXXX:
PROC = {
PutChar[' ];
PutChar['X];
PutChar['X];
PutChar['X];
PutLine[];
};
sep: CHAR ← ' ;
DelHit: ERROR = CODE;
SendNow: PROC = {Stream.SendNow[stream]; flushed ← TRUE};
GetChar:
PROC
RETURNS [
CHAR] = {
c: CHAR;
ignore: INT ← 0;
IF NOT flushed THEN SendNow[];
WHILE ignore >= 0
DO
mark: NAT ← 0;
timingMark: NAT = 5;
timingMarkReply: NAT = 6;
dataMark: NAT = 1;
c ← Stream.GetChar[stream
! Stream.SSTChange => {mark ← sst; CONTINUE};
];
SELECT mark
FROM
0 => NULL;
dataMark => {ignore ← 1};
timingMark => {ignore ← 1; Stream.SetSST[stream, timingMarkReply]};
ENDCASE => ignore ← 2;
ignore ← ignore - 1;
ENDLOOP;
IF c = '\177 THEN {PutXXX[]; ERROR DelHit};
RETURN [c]
};
GetStringToSpace:
PROC [string:
LONG
STRING, stopper1:
CHAR ← ' , stopper2:
CHAR ← ' ] = {
c: CHAR ← GetChar[];
dashCount: NAT ← 0;
inComment: BOOLEAN ← FALSE;
commentHit: BOOLEAN ← FALSE;
string.length ← 0;
UNTIL string.length = string.maxlength
OR (NOT inComment AND (c=stopper1 OR c=stopper2))
OR c='\n
DO
IF c= 'H - 100B
OR c= 'A - 100B
THEN {
IF commentHit THEN {PutXXX[]; ERROR DelHit};
IF string.length > 0
THEN {
IF echo THEN PutChar[c];
string.length ← string.length - 1;
};
}
ELSE IF c= 'W - 100B
THEN {
IF commentHit THEN {PutXXX[]; ERROR DelHit};
WHILE string.length > 0
DO
IF echo THEN PutChar['H - 100B];
string.length ← string.length - 1;
ENDLOOP;
}
ELSE {
IF echo THEN PutChar[c];
IF c = '-
THEN {
commentHit ← TRUE;
dashCount ← dashCount + 1;
IF dashCount = 2
THEN {
inComment ← NOT inComment;
dashCount ← 0;
};
}
ELSE {
WHILE dashCount > 0
DO
IF
NOT inComment
THEN {
string[string.length] ← '-;
string.length ← string.length + 1;
};
dashCount ← dashCount - 1;
ENDLOOP;
IF
NOT inComment
THEN {
string[string.length] ← c;
string.length ← string.length + 1;
};
};
};
c ← GetChar[];
ENDLOOP;
IF string.length <= string.maxlength THEN sep ← c ELSE sep ← ' ;
IF string.length = 0
AND sep # '\n
THEN {
PutChar[sep];
GetStringToSpace[string, stopper1, stopper2];
};
};
GetStringToCR:
PROC [string:
LONG
STRING] = {
GetStringToSpace[string, '\n, '\n];
};
GetNumber:
PROC [default:
INT ← 1]
RETURNS [value:
INT ← 0] = {
string: LONG STRING ← [80];
GetStringToSpace[string];
FOR i:
NAT
IN [0..string.length)
DO
c: CHAR ← string[i];
IF c IN ['0..'9] THEN {value ← value * 10 + (c-'0)}
ELSE {PutString[" Not a number!"]; PutLine[]; ERROR DelHit};
ENDLOOP;
IF string.length = 0 THEN value ← default;
};
PutNumber:
PROC [value:
INT, w:
INTEGER ← 4] = {
IF value < 0 THEN {PutChar['-]; value ← -value; w ← w-1};
IF value >= 10 THEN {PutNumber[value/10, w-1]; value ← value MOD 10; w ← 1};
WHILE w > 1
DO
PutChar[' ];
w ← w - 1;
ENDLOOP;
PutChar['0+value];
};
Confirm:
PROC
RETURNS [yes:
BOOLEAN] = {
response: LONG STRING ← [10];
GetStringToSpace[response];
IF String.Compare[response, "Yes",
FALSE] = 0
THEN {
RETURN [TRUE]
}
ELSE
IF response.length = 0
OR Upper[response[0]] # 'N
THEN {
PutLine[];
PutString["Sorry, you must say Yes in just the right way."];
PutLine[];
RETURN [FALSE]
}
ELSE PutXXX[]
};
GetCommand:
PROC = {
nMatches: NAT ← 0;
matchLength: NAT ← 0;
commandCode ← illegal;
PutString[">>"];
GetStringToSpace[command];
IF command.length = 0 THEN {PutLine[]; GetCommand[]}
ELSE {
FOR cmd: CommandCode
IN [login..quit]
DO
candidate: LONG STRING ← commandTable[cmd];
IF command.length <= candidate.length
THEN {
matchLength ← 0;
FOR i:
NAT
IN [0..command.length)
DO
IF Upper[command[i]] = Upper[candidate[i]] THEN matchLength ← i+1
ELSE EXIT;
ENDLOOP;
IF matchLength = command.length
THEN {
commandCode ← cmd;
IF matchLength = candidate.length THEN {nMatches ← 1; EXIT};
nMatches ← nMatches + 1;
IF NOT loggedIn THEN EXIT
};
};
ENDLOOP;
IF nMatches > 1 THEN commandCode ← ambiguous
ELSE
IF commandCode <= quit
THEN {
candidate: LONG STRING ← commandTable[commandCode];
FOR i:
NAT
IN [command.length..candidate.length)
DO
PutChar[candidate[i]];
ENDLOOP;
};
};
};
PutRequestStatus:
PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = {
PutString[request.requestTime];
PutChar[' ];
PutString[request.fileName];
IF request.copies # 1
THEN {
PutString[" ("];
PutNumber[request.copies, 1];
PutString[" copies) "];
};
PutString[" ("];
PutString[request.requestor];
PutString[") "];
SELECT status
FROM
canceled => PutString["Cancelled"];
waiting => PutString["Waiting"];
printing => PutString["Printing"];
ENDCASE => NULL;
PutLine[];
};
DoCommand:
PROC = {
GetCommand[];
IF
NOT loggedIn
AND commandCode # login
AND commandCode # quit
THEN {
PutLine[];
PutString["Login first, please"];
PutLine[];
}
ELSE {
SELECT commandCode
FROM
login => {
registryMissing: BOOLEAN ← TRUE;
PutString[" --User-- "];
GetStringToSpace[user];
FOR i:
NAT
DECREASING
IN [0..user.length)
DO
IF user[i] = '. THEN {registryMissing ← FALSE; EXIT};
ENDLOOP;
IF registryMissing
THEN {
String.AppendString[user, defaultRegistry];
PutString[defaultRegistry];
};
PutString[" --Password-- "];
echo ← FALSE;
GetStringToSpace[password ! UNWIND => echo ← TRUE];
echo ← TRUE;
IF sep # '\n
THEN {
PutString[" --Account-- "];
GetStringToSpace[account];
};
PutString[" -- Authenticating ... "];
SendNow[];
SELECT NameInfoDefs.Authenticate[user, password]
FROM
individual => {PutString["OK"]; loggedIn ← TRUE};
allDown => {PutString["all GV servers down; I'll have to trust you."]; loggedIn ← TRUE};
badPwd => {PutString["bad password"]; loggedIn ← FALSE};
ENDCASE => {PutString["bad name"]; loggedIn ← FALSE};
PutLine[];
IF loginMessage.length > 0
THEN {
PutString[loginMessage];
PutLine[];
};
};
print => {
fileName: LONG STRING ← [80];
separator: LONG STRING ← [80];
time: LONG STRING ← [40];
requestNumber: INT ← 0;
createDate: LONG STRING ← [40];
bytes: INT ← 0;
copies: INT ← 1;
IF
NOT accessAllowed
THEN {
PutLine[];
PutString["Sorry, you are not allowed to queue requests at this time"];
};
PutString[" --File-- "];
GetStringToSpace[fileName];
IF sep # '\n
THEN {
PutString[" --Copies-- "];
copies ← MIN[GetNumber[default: 1], CARDINAL.LAST];
};
IF sep # '\n
THEN {
PutString[" --Title-- "];
GetStringToCR[separator];
};
bytes ← PDRemoteStream.Lookup[fileName, createDate, user, password !
PDRemoteStream.Error => {
PutLine[];
PutString["Error: "];
PutString[expl];
PutLine[];
GOTO Bad;
};
];
Time.AppendCurrent[time];
requestNumber ← PDQueue.QueueRequest[[fileName, time, user, password, separator, copies]];
IF requestNumber < 0
THEN {
PutLine[];
PutString["Print queue full, request denied."];
PutLine[];
GOTO Bad;
};
PutLine[];
PutString["Print request "];
PutNumber[requestNumber, 0];
PutString[" queued for "];
PutString[fileName];
PutString[" of "];
PutString[createDate];
PutString[" ("];
PutNumber[bytes];
PutString[" bytes)"];
PutLine[];
BEGIN
msg: LONG STRING ← [160];
String.AppendString[msg, "Version of "];
String.AppendString[msg, createDate];
String.AppendString[msg, "; "];
String.AppendLongNumber[msg, bytes];
String.AppendString[msg, " bytes."];
PDQueue.LogMessage[msg, requestNumber];
END;
lastRequest ← requestNumber;
EXITS Bad => NULL
};
check => {
requestNumber: INT;
action:
PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = {
PutLine[];
PutNumber[requestNumber, 5];
PutChar[' ];
IF status = notFound
THEN {
PutString[" not found."];
PutLine[];
}
ELSE PutRequestStatus[request, status];
};
PutString[" --Request Number<"];
IF lastRequest # -1 THEN PutNumber[lastRequest, 1];
PutString[">-- "];
lastRequest ← requestNumber ← GetNumber[default: lastRequest];
IF requestNumber = -1 THEN PutLine[]
ELSE IF ABS[requestNumber] > NAT.LAST THEN action[[NIL,NIL,NIL,NIL,NIL,1], notFound]
ELSE PDQueue.CheckRequest[requestNumber, action];
};
cancel => {
requestNumber: INT;
PutString[" --Request Number-- "];
requestNumber ← GetNumber[default: -1];
IF requestNumber = -1 THEN PutLine[]
ELSE {
owner: LONG STRING ← [80];
reqSatus: PDQueue.RequestStatus;
action:
PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = {
reqSatus ← status;
IF status # notFound
THEN {
String.AppendString[owner, request.requestor];
};
};
PDQueue.CheckRequest[requestNumber, action];
PutLine[];
IF PDQueue.CancelRequest[requestNumber].ok
THEN {
msg: LONG STRING ← [80];
IF reqSatus = printing THEN PDQueue.CancelReprint[];
String.AppendString[msg, "Cancelled by "];
String.AppendString[msg, user];
PDQueue.LogMessage[msg, requestNumber, IF owner.length=0 THEN NIL ELSE owner];
PutString["Print request cancelled."];
}
ELSE {PutString["No such request in queue."]};
PutLine[];
};
};
cancelReprint => {
PDQueue.CancelReprint[];
PutOK[];
};
listQueue => {
action:
PROC [requestNumber:
CARDINAL, request: PDQueue.Request, status: PDQueue.RequestStatus]
RETURNS [continue:
BOOLEAN ←
TRUE] = {
PutNumber[requestNumber, 5];
PutChar[' ];
PutRequestStatus[request, status];
};
PutLine[];
IF PDQueue.GetSuspended[]
THEN {
PutString[" *** Printing is suspended ***"];
PutLine[];
};
PDQueue.EnumerateRequests[action];
};
messages => {
action:
PROC [message:
LONG
STRING]
RETURNS [continue:
BOOLEAN ←
TRUE] = {
IF Match[message]
THEN {
PutString[message];
PutLine[];
};
};
key: LONG STRING ← [80];
Match:
PROC [message:
LONG
STRING]
RETURNS [
BOOLEAN] = {
keyLength: CARDINAL ← key.length;
IF message.length < keyLength THEN RETURN [FALSE];
FOR i:
NAT
IN [0..message.length-key.length)
DO
j: CARDINAL ← 0;
WHILE j # keyLength
AND Upper[message[i+j]] = key[j]
DO
j ← j + 1
ENDLOOP;
IF j=key.length THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
IF sep # '\n
THEN {
PutString[" --matching string-- "];
GetStringToCR[key];
FOR i: NAT IN [0..key.length) DO key[i] ← Upper[key[i]] ENDLOOP;
};
PutLine[];
PDQueue.EnumerateMessages[action];
};
start => {
IF PDQueue.SetSuspended[
FALSE].old =
TRUE
THEN {
PDQueue.LogMessage["Printing started.",,user];
};
PutOK[];
};
stop => {
IF PDQueue.SetSuspended[
TRUE].old =
FALSE
THEN {
PDQueue.LogMessage["Printing suspended.",,user];
};
PutOK[];
};
setLoginMessage => {
old: LONG STRING ← [160];
String.Copy[old, loginMessage];
PutChar[' ];
GetStringToCR[loginMessage];
loginMessage.length ← MIN[loginMessage.length, loginMessage.maxlength-3-user.length];
String.AppendString[loginMessage, " ("];
String.AppendString[loginMessage, user];
String.AppendChar[loginMessage, ')];
PutLine[];
PutString["Old message was: "];
PutString[old];
PutLine[];
PDQueue.LogMessage[loginMessage];
};
reprint => {
ncopies: INT ← 0;
confirm: BOOLEAN ← TRUE;
PutString[" --Number of copies-- "];
ncopies ← GetNumber[default: 0];
IF ncopies > 1
THEN {
PutString[" --Confirm reprint of multiple copies-- "];
confirm ← Confirm[];
};
IF confirm
THEN {
msg: LONG STRING ← [50];
String.AppendString[msg, "Reprint request for "];
String.AppendLongDecimal[msg, ncopies];
IF ncopies = 1 THEN String.AppendString[msg, " copy"]
ELSE String.AppendString[msg, " copies"];
PDQueue.LogMessage[msg,,user];
PDQueue.Reprint[ncopies];
PutOK[];
}
ELSE PutXXX[];
};
resetQueue => {
PutString[" --Do you really want to reset the entire queue?-- "];
IF Confirm[]
THEN {
PutOK[];
PDQueue.Reset[];
PDQueue.LogMessage["Queue reset",,user];
};
};
help, illegal => {
PutLine[];
PutString["Valid commands are: "];
FOR cmd: CommandCode
IN [login..quit]
DO
candidate: LONG STRING ← commandTable[cmd];
PutString[candidate];
IF cmd # quit THEN PutString[", "];
ENDLOOP;
PutLine[];
};
quit => {loggedIn ← FALSE; PutLine[]; SendNow[]; quitting ← TRUE};
wait => {
requestNumber: INT ← lastRequest;
requestStatus: PDQueue.RequestStatus ← waiting;
action:
PROC [request: PDQueue.Request, status: PDQueue.RequestStatus] = {
requestStatus ← status;
};
toldUserThatItIsPrinting: BOOLEAN ← FALSE;
DoPrintingMsg:
PROC = {
IF requestStatus = printing
AND
NOT toldUserThatItIsPrinting
THEN {
PutString["Printing..."];
toldUserThatItIsPrinting ← TRUE;
};
};
IF sep # '\n
THEN {
PutString[" --Request Number<"];
IF lastRequest # -1 THEN PutNumber[lastRequest, 1];
PutString[">-- "];
lastRequest ← requestNumber ← GetNumber[default: lastRequest];
};
PutLine[];
IF requestNumber
IN [0..
NAT.
LAST]
THEN {
PDQueue.CheckRequest[requestNumber, action];
IF requestStatus = waiting
THEN {
PutString["Waiting..."];
};
DoPrintingMsg[];
UNTIL requestStatus = notFound
OR requestStatus = canceled
DO
SendNow[];
Process.Pause[Process.MsecToTicks[10000]];
PDQueue.CheckRequest[requestNumber, action];
DoPrintingMsg[];
PutChar['.];
ENDLOOP;
PutString["Done."];
PutLine[];
};
};
ambiguous => {
PutLine[];
PutString["Ambiguous command (type Help<CR> for help)"];
PutLine[];
};
ENDCASE => ERROR;
};
};
stream.options.signalSSTChange ← TRUE;
PutLine[];
PutString[helloMsg];
PutLine[];
UNTIL loggedIn OR quitting DO DoCommand[! DelHit => CONTINUE] ENDLOOP;
IF loggedIn THEN PDQueue.LogMessage["Login", , user];
WHILE loggedIn DO DoCommand[! DelHit => CONTINUE] ENDLOOP;
PDQueue.LogMessage["Logout", , user];
EXITS Quit => {
IF loggedIn THEN PDQueue.LogMessage["Logged off due to timeout", , user];
};
END;