SendIPMasterImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) September 23, 1985 7:20:31 pm PDT
McCreight, January 30, 1986 5:37:08 pm PST
Bill Jackson (bj) October 9, 1986 5:51:15 am PDT
Some of the credits above are from FSFileCommandsImpl.
DIRECTORY
BasicTime USING [GMT, nullGMT, ToNSTime],
CHNameP2V0 USING [Name],
CHLookUp USING [AddressFromRope],
Commander USING [CommandProc, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
CrRPC USING [BulkDataSource, BulkDataXferProc, CreateClientHandle, DestroyClientHandle, Handle],
File USING [GetVolumeName, SystemVolume],
FileNames USING [ResolveRelativePath],
FS USING [ComponentPositions, Error, ErrorDesc, ExpandName, FileInfo, StreamOpen],
FSPseudoServers USING [TranslateForRead, TranslateForWrite],
IO USING [Close, EndOf, GetBlock, PutBlock, PutRope, STREAM],
PrintingP4V3 USING [GetPrintRequestStatus, Print, PrintAttributes, PrintAttributesObject, PrintOptions, PrintOptionsObject, RequestID, RequestStatus, Time],
PrintingP4V3Aux USING [ExposeRequestID],
Process USING [CheckForAbort],
RefText USING [ObtainScratch, ReleaseScratch],
Rope USING [Cat, Concat, Equal, Fetch, Length, ROPE, Substr],
ThisMachine USING [Name],
UserCredentials USING [Get, CredentialsChangeProc, RegisterForChange],
UserProfile USING [Boolean, CallWhenProfileChanges, Line, Number, ProfileChangedProc],
XNS USING [Address];
SendIPMasterImpl: CEDAR PROGRAM
IMPORTS BasicTime, CHLookUp, Commander, CommandTool, CrRPC, File, FileNames, FS, FSPseudoServers, IO, PrintingP4V3, PrintingP4V3Aux, Process, RefText, Rope, ThisMachine, UserCredentials, UserProfile ~ {
Handle: TYPE ~ CrRPC.Handle;
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
PrintAttributes: TYPE ~ PrintingP4V3.PrintAttributes;
PrintOptions: TYPE ~ PrintingP4V3.PrintOptions;
RequestID: TYPE ~ PrintingP4V3.RequestID;
Time: TYPE ~ PrintingP4V3.Time;
Commander Constants
SendIPMaster: ROPE ~ "///Commands/SendIPMaster";
doc: ROPE ~ "{IPMaster}*\nTransmit a master to an XNS based printer.\nSwitches: <none yet>";
usage: ROPE ~ Rope.Concat["Usage: SendIPMaster ", doc];
UserProfile Tags
DEFSWITCHES: ROPE ~ "SendIPMaster.DefaultSwitchesLine";
BANNER: ROPE ~ "Hardcopy.Banner";
CFNAME: ROPE ~ "Hardcopy.CFName";
COPYCOUNT: ROPE ~ "Hardcopy.CopyCount";
CREATEDATE: ROPE ~ "Hardcopy.CreateDate";
IPPRINTER: ROPE ~ "Hardcopy.InterpressPrinter";
KEY: ROPE ~ "Hardcopy.Key";
MEDIUM: ROPE ~ "Hardcopy.Medium";
PIGEONHOLE: ROPE ~ "Hardcopy.PigeonHole";
PAGEFIRST: ROPE ~ "Hardcopy.PageFirst";
PAGELAST: ROPE ~ "Hardcopy.PageLast";
PRINTSIZE: ROPE ~ "Hardcopy.PrintSize";
PRIORITY: ROPE ~ "Hardcopy.Priority";
STAPLED: ROPE ~ "Hardcopy.Stapled";
TWOSIDED: ROPE ~ "Hardcopy.TwoSided";
XferObject, one per callback in progress;
PrintRequest: TYPE ~ REF PrintRequestObject;
PrintRequestObject: TYPE ~ RECORD [
attributes: PrintAttributes,
ipMasterStream: STREAM,
options: PrintOptions,
requestID: RequestID,
service: ROPE];
Environs: TYPE ~ REF EnvironsObject;
EnvironsObject: TYPE ~ RECORD [
copyCount: INT,
message: ROPE,
mediumHint: ROPE,
pageFirst: INT,
pageLast: INT,
printerName: ROPE,
printObjectCreateDate: Time,
printObjectName: ROPE,
printObjectSize: INT,
priorityHint: ROPE,
recipientName: ROPE,
releaseKey: INT,
senderName: ROPE,
stapled: BOOL,
twoSided: BOOL
];
staticEnvirons: Environs;
I/O Procs
DecodeRequestID: PROC [out: STREAM, requestID: RequestID] ~ {
msg: ROPE;
msg ← Rope.Concat[PrintingP4V3Aux.ExposeRequestID[requestID, 0], "\n"];
IO.PutRope[out, msg];
};
FSErrorMsg: PROC [error: FS.ErrorDesc] RETURNS [ROPE] ~ {
RETURN[Rope.Concat[FSErrorMsgBrief[error], "\n"]];
};
FSErrorMsgBrief: PROC [error: FS.ErrorDesc] RETURNS [ROPE] ~ {
SELECT error.group FROM
lock => {
RETURN[" -- locked!"];
};
ENDCASE => {
IF (error.code = $unknownFile)
THEN RETURN [" -- not found!"]
ELSE RETURN[Rope.Concat["\n -- FS.Error: ", error.explanation]];
};
};
Helper PROCs
FixUpFilename: PROC [name: ROPENIL, copyFrom: BOOL] RETURNS [newName: ROPE] ~ {
body: ROPE;
platformHost: ROPE ← ThisMachine.Name[];
platformVolume: ROPE ← File.GetVolumeName[File.SystemVolume[]];
prefix: ROPE;
cp: FS.ComponentPositions;
[fullFName: newName, cp: cp] ← FS.ExpandName[name];
prefix ← Rope.Substr[newName, 0, 4];
body ← Rope.Substr[newName, 4, Rope.Length[newName]];
IF Rope.Equal[prefix, "[]<>"] THEN {
newName ← Rope.Cat["[", platformHost, "]<", platformVolume, ">"];
newName ← Rope.Cat[newName, body];
}
ELSE newName ← Rope.Cat[
"[",
IF copyFrom THEN
FSPseudoServers.TranslateForRead[Rope.Substr[newName, cp.server.start, cp.server.length]].first
ELSE
FSPseudoServers.TranslateForWrite[Rope.Substr[newName, cp.server.start, cp.server.length]],
"]<",
Rope.Substr[newName, cp.dir.start, Rope.Length[newName]]];
};
GetAttributes: PROC [environs: Environs] RETURNS [attributes: PrintAttributes] ~ {
attributes ← NEW[ PrintingP4V3.PrintAttributesObject[3] ];
TRUSTED { attributes.body[0] ← [printObjectName[environs.printObjectName]] };
TRUSTED { attributes.body[1] ← [printObjectCreateDate[environs.printObjectCreateDate]] };
TRUSTED { attributes.body[2] ← [senderName[environs.senderName]] };
};
GetDefaults: PROC RETURNS [environs: Environs] ~ {
environs ← NEW[EnvironsObject];
environs.senderName ← UserCredentials.Get[].name;
environs.message ← UserProfile.Line[BANNER, ""];
environs.printObjectName ← UserProfile.Line[CFNAME, "No File Name"];
environs.copyCount ← UserProfile.Number[COPYCOUNT, 1];
environs.printObjectCreateDate ← BasicTime.ToNSTime[BasicTime.nullGMT];
environs.printObjectCreateDate ← UserProfile.Line[CREATEDATE, ""];
environs.printerName ← UserProfile.Line[IPPRINTER, "Huh? -- no printer!"];
environs.releaseKey ← UserProfile.Number[KEY, 0];
environs.mediumHint ← UserProfile.Line[MEDIUM, "paper"];
environs.recipientName ← UserProfile.Line[PIGEONHOLE, environs.senderName];
environs.pageFirst ← UserProfile.Number[PAGEFIRST, 1];
environs.pageLast ← UserProfile.Number[PAGELAST, 177777B];
environs.printObjectSize ← UserProfile.Number[PRINTSIZE, 0];
environs.priorityHint ← UserProfile.Line[PRIORITY, "normal"];
environs.stapled ← UserProfile.Boolean[STAPLED, FALSE];
environs.twoSided ← UserProfile.Boolean[TWOSIDED, TRUE];
};
GetRequest: PROC [environs: Environs] RETURNS [request: PrintRequest] ~ {
request ← NEW[PrintRequestObject];
request.attributes ← GetAttributes[environs];
request.ipMasterStream ← NIL;
request.options ← GetOptions[environs];
request.requestID ← ALL[0];
request.service ← environs.printerName;
};
GetOptions: PROC [environs: Environs] RETURNS [options: PrintOptions] ~ {
options ← NEW[ PrintingP4V3.PrintOptionsObject[5] ];
TRUSTED { options.body[0] ← [recipientName[environs.recipientName]] };
TRUSTED { options.body[1] ← [message[environs.message]] };
TRUSTED { options.body[2] ← [copyCount[environs.copyCount]] };
TRUSTED { options.body[3] ← [staple[environs.stapled]] };
TRUSTED { options.body[4] ← [twoSided[environs.twoSided]] };
TRUSTED { options.body[0] ← [printObjectSize[CARD]] };
TRUSTED { options.body[1] ← [recipientName[ROPE]] };
TRUSTED { options.body[2] ← [message[ROPE]] };
TRUSTED { options.body[3] ← [copyCount[CARDINAL]] };
TRUSTED { options.body[4] ← [pagesToPrint[[beginningPageNumber, endingPageNumber: CARDINAL]]] };
TRUSTED { options.body[5] ← [mediumHint[Medium]] };
TRUSTED { options.body[6] ← [priorityHint[Priority]] };
TRUSTED { options.body[7] ← [releaseKey[XNSAuth.HashedPassword]] };
TRUSTED { options.body[8] ← [staple[BOOL]] };
TRUSTED { options.body[9] ← [twoSided[BOOL]] };
};
Active PROCs
GetPrintRequestStatus: PROC [destRope: ROPE, printRequestID: RequestID]
RETURNS [states: PrintingP4V3.RequestStatus] ~ {
addr: XNS.Address;
h: Handle;
service: CHNameP2V0.Name;
[addr, service] ← CHLookUp.AddressFromRope[destRope];
h ← CrRPC.CreateClientHandle[class~$SPP, remote~addr];
states ← PrintingP4V3.GetPrintRequestStatus[h, printRequestID];
CrRPC.DestroyClientHandle[h];
};
Print: PROC [request: PrintRequest] ~ {
addr: XNS.Address;
h: Handle;
master: CrRPC.BulkDataSource;
service: CHNameP2V0.Name;
[addr, service] ← CHLookUp.AddressFromRope[request.service];
master ← Xfer;
h ← CrRPC.CreateClientHandle[class~$SPP, remote~addr];
h.clientData ← request;
request.requestID ← PrintingP4V3.Print[h, master, request.attributes, request.options];
CrRPC.DestroyClientHandle[h];
};
Xfer: CrRPC.BulkDataXferProc
[h: Handle, stream: IO.STREAM, checkAbort: BulkDataCheckAbortProc] RETURNS [abort: BOOL]
~ {
bufSize: NAT ~ 1024;
buf: REF TEXT ~ RefText.ObtainScratch[bufSize];
request: PrintRequest ← NARROW [h.clientData];
abort ← FALSE;
DO
IF IO.EndOf[request.ipMasterStream] THEN EXIT;
IF (abort ← checkAbort[h]) THEN EXIT;
[] ← IO.GetBlock[request.ipMasterStream, buf];
IO.PutBlock[stream, buf];
ENDLOOP;
RefText.ReleaseScratch[buf];
};
Commander PROCs
SendMaster: Commander.CommandProc ~ {
out: STREAM ~ cmd.out;
environ: Environs;
request: PrintRequest;
switches: PACKED ARRAY CHAR['a..'z] OF BOOLALL[FALSE];
ProcessSwitches: PROC [environ: Environs, arg: ROPE] ~ {
sense: BOOLTRUE;
FOR index: INT IN [0..Rope.Length[arg]) DO
char: CHAR ← Rope.Fetch[arg, index];
SELECT char FROM
'- => LOOP;
'~ => {sense ← NOT sense; LOOP};
IN ['a..'z] => switches[char] ← sense;
IN ['A..'Z] => switches[char + ('a-'A)] ← sense;
ENDCASE;
sense ← TRUE;
ENDLOOP;
};
ProcessArgument: PROC [arg: ROPE] ~ {
argsProcessed ← argsProcessed + 1;
Process.CheckForAbort[];
To process an argument,
open the file, and send its contents to the printer,
generate some sensible textual output about the request ID.
request.ipMasterStream ← FS.StreamOpen[FileNames.ResolveRelativePath[arg]
! FS.Error => IF (error.group # $bug) THEN {msg ← FSErrorMsg[error]; ERROR CommandTool.Failed[msg]}];
Print[request]; -- i'm unsure of using the requestID side effect, but..
DecodeRequestID[out, request.requestID];
IF (request.ipMasterStream # NIL) THEN IO.Close[request.ipMasterStream];
};
argsProcessed: NAT ← 0;
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd, starExpand: FALSE
! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
environ ← NEW[EnvironsObject ← staticEnvirons^];
ProcessSwitches[environ, UserProfile.Line[DEFSWITCHES]];
Allows the user to specify personal defaults for the switches via the user's profile.
FOR i: NAT IN [1..argv.argc) DO
arg: ROPE ~ argv[i];
Process.CheckForAbort[];
IF (Rope.Length[arg] = 0) THEN LOOP;
IF (Rope.Fetch[arg, 0] = '-) THEN {
ProcessSwitches[environ, arg];
LOOP;
};
environ.printObjectName ← FixUpFilename[arg, FALSE];
{
fullFName: ROPE;
attachedTo: ROPE;
keep: CARDINAL;
bytes: INT;
created: BasicTime.GMT;
[fullFName, attachedTo, keep, bytes, created] ← FS.FileInfo[name~arg, remoteCheck~FALSE];
environ.printObjectCreateDate ← BasicTime.ToNSTime[created];
};
request ← GetRequest[environ];
IO.PutRope[out, Rope.Cat["Sending: ", environ.printObjectName, "\n"]];
ProcessArgument[arg];
request ← NIL; -- we should cache these for later status requests!
ENDLOOP;
SELECT argsProcessed FROM
0 => msg ← usage;
ENDCASE => NULL;
EXITS
failed => {result ← $Failure};
};
Snoopy PROCs
NewProfile: UserProfile.ProfileChangedProc ~ {
staticEnvirons ← GetDefaults[];
};
NewUser: UserCredentials.CredentialsChangeProc ~ {
staticEnvirons ← GetDefaults[];
};
Init Proc
Init: PROC ~ {
Commander.Register[key: SendIPMaster, proc: SendMaster, doc: doc, clientData: NIL, interpreted: TRUE];
UserProfile.CallWhenProfileChanges[NewProfile];
UserCredentials.RegisterForChange[NewUser];
};
Init[];
}...