<> <> <> <<>> 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: BOOLEAN _ FALSE; 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 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.