<> <> <> DIRECTORY Basics, Commander, Convert, Rope, IO, FS, MessageWindow, PDInterpBasic, PDPrinterRpcControl, PDPrinter, PDPrinterClient, PieViewers, RPC, LupineRuntime, Process, ViewerOps; PDPrinterClientImpl: CEDAR PROGRAM IMPORTS Commander, Convert, IO, FS, MessageWindow, PDPrinterRpcControl, PDPrinter, Rope, RPC, LupineRuntime, PieViewers, Process, ViewerOps EXPORTS PDPrinterClient ~ BEGIN ROPE: TYPE ~ Rope.ROPE; mess: CARDINAL _ 0; copiesFieldIndex: NAT ~ 10; BinaryStreamOptions: PROC RETURNS [s: FS.StreamOptions] ~ { s _ FS.defaultStreamOptions; s[tiogaRead] _ FALSE; }; GetToken: PROC [stream: IO.STREAM] RETURNS [token: ROPE _ NIL] = { token _ stream.GetTokenRope[Break ! IO.EndOfStream => CONTINUE].token; }; Break: PROC [char: CHAR] RETURNS [IO.CharClass] = { IF char = '_ OR char = '; THEN RETURN [break]; IF char = ' OR char = ' OR char = ', OR char = '\n THEN RETURN [sepr]; RETURN [other]; }; IsInteger: PROC [rope: ROPE] RETURNS [BOOLEAN] ~ { IF NOT rope.Length IN [1..3] THEN RETURN [FALSE]; FOR i: INT IN [0..rope.Length) DO IF rope.Fetch[i] NOT IN ['0..'9] THEN RETURN [FALSE]; ENDLOOP; RETURN [TRUE]; }; NotAPDFile: PUBLIC ERROR ~ CODE; CheckPassword: PROC [word0: CARDINAL] ~ {IF word0 # 0AAAAH THEN ERROR NotAPDFile}; Print: PUBLIC PROC [file: Rope.ROPE, server: Rope.ROPE, copies: NAT _ 1, report: IO.STREAM _ NIL, createFeedbackViewer: BOOLEAN _ TRUE] ~ TRUSTED { serverRName: Rope.ROPE = server.Concat[".auto"]; interfaceName: RPC.InterfaceName _ [instance: serverRName]; pageNo: INT _ 0; ReportStatus: SAFE PROC [status: PDInterpBasic.Status, report: IO.STREAM _ NIL] ~ CHECKED { stream: IO.STREAM _ IF report = NIL THEN IO.ROS[] ELSE report; SELECT status FROM constructingImage => stream.PutF[" [%g", IO.int[pageNo _ pageNo + 1]]; betweenPages => {IF pageNo > 0 THEN stream.PutChar[']]}; transmissionComplete => stream.PutRope[" done"]; ENDCASE => {stream.PutChar[' ]; stream.Put[IO.refAny[NEW[PDInterpBasic.Status _ status]]]}; IF report = NIL THEN { MessageWindow.Append[IO.RopeFromROS[stream], mess MOD 4 = 0]; mess _ mess + 1; }; }; stream: IO.STREAM _ FS.StreamOpen[fileName: file, streamOptions: BinaryStreamOptions[] ! FS.Error => TRUSTED { s: IO.STREAM _ IO.ROS[]; s.PutRope["Unable to open PD file: "]; s.PutRope[error.explanation]; s.PutRope[" ("]; s.Put[IO.rope[file]]; s.PutRope[")"]; IF report = NIL THEN { MessageWindow.Append[IO.RopeFromROS[s], TRUE]; MessageWindow.Blink[]; } ELSE { report.PutRope[IO.RopeFromROS[s]]; }; GOTO Quit; } ]; response: PDPrinter.Response _ [nextWordIndex: 0, status: betweenPages, currentPage: 0]; dataBlock: PDPrinter.DataBlock; oldStatus: PDInterpBasic.Status _ transmissionComplete; bufferPointer: LONG POINTER _ @dataBlock.buffer; pieViewer: PieViewers.PieViewer _ NIL; fileSize: INT = stream.GetLength; PDPrinterRpcControl.ImportInterface[interfaceName ! RPC.ImportFailed => { s: IO.STREAM _ IO.ROS[]; s.PutRope["Unable to access PDInterpControl: "]; s.Put[IO.refAny[NEW[RPC.ImportFailure _ why]]]; IF report = NIL THEN { MessageWindow.Append[IO.RopeFromROS[s], TRUE]; MessageWindow.Blink[]; } ELSE { report.PutRope[IO.RopeFromROS[s]]; }; GOTO Quit; } ]; IF createFeedbackViewer THEN { pieViewer _ PieViewers.Create[parent: NIL, diameter: 0, total: fileSize]; pieViewer.name _ file.Concat[" to "].Concat[server]; pieViewer.icon _ private; }; BEGIN ENABLE UNWIND => {PDPrinterRpcControl.UnimportInterface[]; IO.Close[stream]; IF pieViewer # NIL THEN {ViewerOps.DestroyViewer[pieViewer]; pieViewer _ NIL}}; WHILE response.nextWordIndex >= 0 DO byteIndex: INT _ response.nextWordIndex*2; bytesRead: INT _ 0; bytesPerBlock: INT = PDPrinter.maxBlockSize*Basics.bytesPerWord; stream.SetIndex[byteIndex]; bytesRead _ stream.UnsafeGetBlock[[bufferPointer, 0, bytesPerBlock]]; dataBlock.wordIndex _ response.nextWordIndex; dataBlock.wordCount _ bytesRead/Basics.bytesPerWord; IF (copiesFieldIndex-dataBlock.wordIndex) IN [0..dataBlock.wordCount) THEN { dataBlock.buffer[copiesFieldIndex-dataBlock.wordIndex] _ copies; }; IF byteIndex = 0 THEN CheckPassword[dataBlock.buffer[0]]; IF pieViewer # NIL THEN PieViewers.Set[pieViewer, fileSize-byteIndex]; response _ PDPrinter.TransmitBlock[dataBlock]; IF response.status # oldStatus THEN { ReportStatus[response.status, report]; oldStatus _ response.status; }; ENDLOOP; END; IF pieViewer # NIL THEN PieViewers.Set[pieViewer, 0]; PDPrinterRpcControl.UnimportInterface[]; IO.Close[stream]; IF pieViewer # NIL THEN {ViewerOps.DestroyViewer[pieViewer]; pieViewer _ NIL} EXITS Quit => NULL; }; PeachCommand: Commander.CommandProc ~ { cmdStream: IO.STREAM _ IO.RIS[cmd.commandLine]; server: ROPE _ GetToken[cmdStream]; copies: NAT _ 1; DO token: ROPE _ GetToken[cmdStream]; IF token.Length = 0 THEN EXIT; IF IsInteger[token] THEN copies _ Convert.IntFromRope[token] ELSE { fileName: ROPE; cp: FS.ComponentPositions; [fileName, cp] _ FS.ExpandName[token ! FS.Error => { IF cmd.out = NIL THEN REJECT ELSE {cmd.out.PutRope[fileName]; cmd.out.PutRope[": "]; cmd.out.PutRope[error.explanation]; CONTINUE} }]; IF fileName = NIL THEN LOOP; IF cp.ext.length = 0 THEN { fileName _ fileName.Replace[cp.ext.start, cp.ext.length, ".pd"]; }; IF cmd.out # NIL THEN { cmd.out.PutRope["Sending "]; cmd.out.PutRope[fileName]; IF copies # 1 THEN { cmd.out.PutF[" (%g copies)", IO.int[copies]]; }; cmd.out.PutRope[" to "]; cmd.out.PutRope[server]; cmd.out.PutRope[" . . ."]; }; Print[fileName, server, copies, cmd.out ! LupineRuntime.BindingError => {IF cmd.out # NIL THEN cmd.out.PutRope[" ."]; Process.Pause[Process.MsecToTicks[5000]]; RETRY}; NotAPDFile => {IF cmd.out # NIL THEN cmd.out.PutRope[" not a PD file"]; CONTINUE}; ]; IF cmd.out # NIL THEN { cmd.out.PutRope[";\n"]; }; }; ENDLOOP; }; Commander.Register["Peach", PeachCommand, "Usage: Peach {[ ] }, e.g. Peach Melba my 2 foo bar 1 baz sends one copy of my.pd, 2 of foo.pd and bar.pd, and one of baz.pd" ]; END.