PDUserImpl.mesa
Copyright (C) 1984, Xerox Corporation. All rights reserved.
Michael Plass, October 31, 1984 10:18:59 am PST
DIRECTORY PDQueue, PDUser, Process, Stream, Time, PDRemoteStream, NameInfoDefs, String;
PDUserImpl: PROGRAM
IMPORTS PDQueue, Process, Stream, Time, PDRemoteStream, NameInfoDefs, String
EXPORTS PDUser
= BEGIN
helloMsg: LONG STRING ← "Peach PD Print Server";
CommandCode: TYPE = {login, cancel, cancelReprint, check, help, listQueue, messages, print, reprint, resetQueue, setLoginMessage, start, stop, wait, quit, ambiguous, illegal};
commandTable: ARRAY CommandCode[login..quit] OF LONG STRING ← [
login: "Login",
cancel: "Cancel",
cancelReprint: "CancelReprint",
check: "Check",
help: "Help",
listQueue: "ListQueue",
messages: "Messages",
print: "Print",
reprint: "Reprint",
resetQueue: "ResetQueue",
setLoginMessage: "SetLoginMessage",
start: "Start",
stop: "Stop",
wait: "Wait",
quit: "Quit"
];
Upper: PROC [ch: CHAR] RETURNS [CHAR] = INLINE {
RETURN [IF ch IN ['a..'z] THEN ch - ('a - 'A) ELSE ch]
};
defaultRegistry: LONG STRING ← ".pa"L;
loginMessage: LONG STRING ← [160];
TalkWithUser: PUBLIC PROC [stream: Stream.Handle] = {
user: LONG STRING ← [80];
loggedIn: BOOLEANFALSE;
BEGIN ENABLE Stream.EndOfStream, Stream.TimeOut => GOTO Quit;
command: LONG STRING ← [80];
password: LONG STRING ← [80];
account: LONG STRING ← [80];
quitting: BOOLEANFALSE;
accessAllowed: BOOLEANTRUE;
echo: BOOLEANTRUE;
flushed: BOOLEANFALSE;
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: BOOLEANFALSE;
commentHit: BOOLEANFALSE;
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: BOOLEANTRUE;
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: BOOLEANTRUE] = {
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: BOOLEANTRUE] = {
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: BOOLEANTRUE;
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: BOOLEANFALSE;
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;
};
END.