DIRECTORY BasicTime USING [GMT, nullGMT], IO USING [STREAM], Rope USING [ROPE], PrintingP4V3; XNSPrint: CEDAR DEFINITIONS ~ { OPEN Printing: PrintingP4V3; ROPE: TYPE ~ Rope.ROPE; FormatterStatus: TYPE ~ Printing.FormatterStatus; InterpressMasterStatus: TYPE ~ Printing.InterpressMasterStatus; MarkingEngineStatus: TYPE ~ Printing.MarkingEngineStatus; PaperDimensions: TYPE ~ Printing.PaperDimensions; SpoolerStatus: TYPE ~ Printing.SpoolerStatus; Problem: TYPE ~ { connection, -- network transport error file, -- some FS error, such as the named file does not exist name, -- address database does not contain this host name protocol, -- fatal, somebody got their spec confused! service, -- printer (totally) unable to complete request serviceRetry, -- printer (temporarily) unable to complete request stream, -- some stream error encountered when shipping data unknown -- unexpected transport error, and catchall }; Error: ERROR [problem: Problem, explanation: ROPE]; FIREWALL: ERROR; -- nobody should see and/or catch this one! Media: TYPE ~ LIST OF AvailableKind; AvailableKind: TYPE ~ ROPE; -- considered opaque, the rope is a convenience GetPaperDimensions: PROC [s: AvailableKind] RETURNS [size: PaperDimensions ¬ [0, 0]]; ListAvailableKinds: PROC [] RETURNS [list: Media ¬ NIL]; Properties: TYPE ~ RECORD [ media: Media, staple: BOOL, twoSided: BOOL ]; GetPrinterProperties: PROC [printer: ROPE] RETURNS [service: ROPE, answer: Properties]; PrinterStatus: TYPE ~ RECORD [ spooler: SpoolerStatus, formatter: FormatterStatus, printer: MarkingEngineStatus, media: Media ]; GetPrinterStatus: PROC [printer: ROPE] RETURNS [service: ROPE, answer: PrinterStatus]; StatusWatcherProc: TYPE ~ PROC [newStatus: PrinterStatus, clientData: REF, watcher: StatusWatcher]; RegisterStatusWatcher: PROC [printer: ROPE, update: StatusWatcherProc, clientData: REF ¬ NIL] RETURNS [watcher: StatusWatcher]; UnRegisterStatusWatcher: PROC [watcher: StatusWatcher]; Context: TYPE ~ REF ContextObject; ContextObject: TYPE ~ RECORD [ copyCount: CARD16 ¬ 1, mediumHint: AvailableKind ¬ NIL, -- "usLetter" message: ROPE ¬ NIL, pageFirst: CARD16 ¬ 0, pageLast: CARD16 ¬ LAST [CARD16], printerName: ROPE ¬ NIL, printObjectCreateDate: BasicTime.GMT ¬ BasicTime.nullGMT, printObjectName: ROPE ¬ NIL, printObjectSize: INT ¬ 0, priorityHint: ROPE ¬ NIL, recipientName: ROPE ¬ NIL, releaseKey: CARD16 ¬ 0, senderName: ROPE ¬ NIL, -- UserCredentials.Get[].name stapled: BOOL ¬ TRUE, telephone: ROPE ¬ NIL, twoSided: BOOL ¬ TRUE ]; GetDefaults: PROC [context: Context ¬ NIL] RETURNS [newContext: Context]; StatusChangedProc: TYPE ~ PROC [request: PrintRequest, clientData: REF]; PrintFromFile: PROC [file: ROPE, context: Context, update: StatusChangedProc ¬ NIL, clientData: REF ¬ NIL] RETURNS [request: PrintRequest]; PrintFromStream: PROC [s: IO.STREAM, context: Context, update: StatusChangedProc ¬ NIL, clientData: REF ¬ NIL] RETURNS [request: PrintRequest]; RequestStatus: TYPE ~ RECORD [ status: InterpressMasterStatus, statusMessage: ROPE ]; GetPrintRequestStatus: PROC [request: PrintRequest] RETURNS [status: RequestStatus]; RegisterPrintRequest: PROC [request: PrintRequest, update: StatusChangedProc, clientData: REF ¬ NIL]; UnRegisterPrintRequest: PROC [request: PrintRequest]; StatusWatcherList: TYPE ~ LIST OF StatusWatcher; StatusWatcher: TYPE ~ REF StatusWatcherObject; StatusWatcherObject: TYPE ~ RECORD [ clientData: REF, -- must be NIL'ed out during UnRegisterStatusWatcher[] lastStatus: PrinterStatus, update: StatusWatcherProc ]; WatcherListChanged: CONDITION; GetWatcherList: PROC RETURNS [list: PrintRequestList]; PrintRequestList: TYPE ~ LIST OF PrintRequest; PrintRequest: TYPE ~ REF PrintRequestObject; PrintRequestObject: TYPE ~ RECORD [ clientData: REF, -- must be NIL'ed out during UnRegisterPrintRequest[] context: Context, -- a copy of the ContextObject used to create this PrintRequestObject update: StatusChangedProc, distinguishedName: ROPE, requestID: Printing.RequestID, lastStatus: RequestStatus, attributes: Printing.PrintAttributes, options: Printing.PrintOptions, fileName: ROPE, ipMasterStream: IO.STREAM ]; RequestListChanged: CONDITION; GetPrintRequestList: PROC RETURNS [list: PrintRequestList]; CreateAvailableKind: PROC [s: Printing.KnownPaperSize] RETURNS [kind: AvailableKind]; CreateAvailableKindOther: PROC [size: PaperDimensions] RETURNS [kind: AvailableKind]; CreateMedium: PROC [s: AvailableKind] RETURNS [medium: Printing.Medium]; MungeMedia: PROC [s: Printing.Media] RETURNS [media: Media]; Insert: PROC [key: ROPE, medium: Printing.Medium, size: PaperDimensions]; CreateAttributes: PROC [context: Context] RETURNS [attributes: Printing.PrintAttributes]; CreateOptions: PROC [context: Context] RETURNS [options: Printing.PrintOptions]; CreatePrintRequest: PROC [context: Context, copyContext: BOOL ¬ TRUE] RETURNS [request: PrintRequest]; MungeProperties: PROC [s: Printing.PrinterProperties] RETURNS [properties: Properties]; MungePrinterStatus: PROC [s: Printing.PrinterStatus] RETURNS [printerstatus: PrinterStatus]; MungeRequestStatus: PROC [s: Printing.RequestStatus] RETURNS [requeststatus: RequestStatus]; }... ¬ XNSPrint.Mesa Copyright Σ 1985, 1986, 1987, 1992 by Xerox Corporation. All rights reserved. Bill Jackson (bj) April 9, 1987 1:12:34 am PDT Tim Diebert: February 9, 1987 9:55:21 am PST Prolog: XNSPrint is a veneer over the XNS Printing protocol XSIS 118404. An attempt has been made here to totally isolate clients from machine generated code, although you'll probably have to have the BCD around because of the compiler's symbol copying mechanisms. It may seem a little bizarre at first that things are so complex, such as the status callbacks, but the attempt here is to keep the vulnerabilities of the printers (such as limited calls/sec) isolated from clients while still providing rich and responsive operations. It may take a little extra code, but by using the registration mechanism, you'll be a good network citizen. copied for implementation convenience Things that can go bump in the nite! The general idea here is that everything but serviceRetry is pretty disgusting, and should be relayed to your "customer". If you get a serviceRetry, then you probably want to register with the status watcher, and try again when the printer feels better... An abstract representation of printer "Media" Media is a little sticky. The print protocol deals with real messy data types, so the idea here is to make things simple for clients. To this end, we traffic here in "AvailableKind"s which are represented by ropes that are registered in an internal symbol table along with the *real* representation for the print protocol, and with the paper sizes (taken from the standards document). Clients should not create kinds of their own, but rather should use values which have validly been returned by some printer, or are well known. A preferred approach is to ask a printer what it can do before creating a master to be printed there... Return the size of this medium in millimeters. A length of 0 means roll paper (indefinite length). A width of 0 will be returned if the medium is not (currently) known. Returns the current list (alphabetized of course). Well know types get registered initially, so before we ever check with a printer, the standard ones get listed. As we acquire more knowledge, the list grows, and grows... Discovering and monitoring the printer "state" REPORTS [ServiceUnavailable, SystemError, Undefined] REPORTS [ServiceUnavailable, SystemError, Undefined] The watcher here is you, so that you can unregister during the callback Adds update to the data base of individuals interested in learning the state of the printer when it next changes. Since there's a race condition here, the initial condition is that a callback will occur at registration time (since the status is initially unknown). It is required that RegisterStatusWatcher persist until the request is unregistered. Update may do the unregister (I promise not to monitor lock this way...). Remove watcher from the data base. Contextual information for request interpretation: In order to simply the basic printing operations, and to simplify keeping track of user defined quantities (thru the user profile) requests are serviced with a "Context", which is used to satisfy secondary request options. The basic idea here is that you're probably going to use a context over and over again, so you might just as well hang on to it. If you're not watching for user profile changes you might decide to pay the price to always refresh this information rather than "caching" a copy. Proper semantics are to always get contexts from GetDefaults[NIL], or refreshing them with GetDefaults[context]. ContextObjects belong to the creator (the person invoking GetDefaults), so the object is yours and you can modify it to your hearts content. An interesting sequence might look like: context _ GetDefaults[NIL]; context.printerName _ "Quoth"; -- a request to use a special printer context _ GetDefaults[context]; -- return to the printerName in the user's profile. decorates ContextObject from current user's defaults. Supplying NIL causes a context to be allocated, otherwise, its content is altered. Making and monitoring print requests The request may be used to unregister at callback time. REPORTS [ServiceUnavailable, SystemError, Undefined] REPORTS [ServiceUnavailable, SystemError, Undefined] If update is non-NIL, then RegisterPrintRequest will be invoked. All of the commentary below on RegisterPrintRequest applies. REPORTS [ServiceUnavailable, SystemError, Undefined] Add request to the data base of pending requests for possible use by other applications. If update is non-NIL, then status updates will be performed, and the StatusChangedProc will be invoked (asynchronously) when lastStatus changes; lastStatus is initialized in such a way as to have the first status probe generate a callback. This requires that StatusChangedProc persist until the request is unregistered. Update may do the unregister (I promise not to monitor lock this way...). Remove request from the data base of pending requests. Private Stuff, those not faint of heart can read on... StatusWatcher Objects belong to the implementation. It's considered stretching the rules to be poking around inside. Notified whenever someone either registers or unregisters a request Atomically return the data base of pending requests. The intent is that it will be (almost) immediately traversed. Updates will happen behind the scenes, so this represents some state from the past. Safe Storage provides adequate guarantees for this. PrintRequestObjects have some internal constraints depending upon how this interface is implemented. Considering this, you should probably consider them immutable, unless of course you're sure that you own them. The intent is to capture all of the state required external to the implementation so that clients can examine the values, and to make debugging and/or exception handing easier along with overloading to avoid another totally private data type. Notified whenever someone either registers or unregisters a request Atomically return the data base of pending requests. The intent is that it will be (almost) immediately traversed. Updates will happen behind the scenes, so this represents some state from the past. Safe Storage provides adequate guarantees for this. Κ θ•NewlineDelimiter –(cedarcode) style™codešœ ™ Kšœ ΟeœC™NKšœ.™.K™,—K˜šΟk ˜ Kšœ žœžœ ˜Kšžœžœžœ˜Kšœžœžœ˜Kšœ ˜ —K˜šΟnœžœž œ˜KšžœŸœ˜Kšžœžœžœ˜K˜šœ™K™K™K™K™ψK™—šœ%™%K™Kšœžœ˜1Kšœžœ#˜?Kšœžœ ˜9Kšœžœ˜1Kšœžœ˜-K˜—™$J™Jšœ€™€J™–%clearTabStops 20 sp flushLeft tabStopšœ žœ˜K–%clearTabStops 20 sp flushLeft tabStopšœ Οc˜&K–%clearTabStops 20 sp flushLeft tabStopšœ 7˜=K–%clearTabStops 20 sp flushLeft tabStopšœ 3˜9K–%clearTabStops 20 sp flushLeft tabStopšœ  +˜5K–%clearTabStops 20 sp flushLeft tabStopšœ  /˜8K–%clearTabStops 20 sp flushLeft tabStopšœ 3˜AK–%clearTabStops 20 sp flushLeft tabStopšœ œ &˜