PrintQueueImpl.mesa
Copyright (C) Xerox Corporation 1981, 1982, 1983, 1984, 1985, 1986. All rights reserved.
Last edited by Jacks: 14-Oct-85 17:22:25
Tim Diebert: December 29, 1986 10:00:25 am PST
<<Print Server's internal queue interface for Spooled PrintObjects.
Note: current implementation is simplistic and probably very inefficient; it is hoped
that the internals can eventually be upgraded without affecting the externals.
Need:
Improved protection against linking queues in circles (like the case of Requeueing to the same queue).
What about File, Directory and Volume Errors???
Fix Space allocation and QueueObjectPointer; should be BASE or RELATIVE pointer???
Simplify qIndex and make it a range TYPE [0..max]; make queue ARRAY [0..max] and make queue[max]
a null. Thus all references to nullQueueIndex will point to the null entry.
Atomic snap shot of queue for listing and status. Maybe a List proc which receives a STRING
SEQUENCE and returns the file names and status in it.>>
DIRECTORY
BasicTime USING [Now],
FS USING [Close, Copy, Create, Delete, Error, GetName, maxFNameLength, nullOpenFile, Open, OpenFile],
IO USING [STREAM],
NSString USING [AppendString, String, StringFromMesaString, StringRep, TruncateString],
PrintQueue,
PrintingTypes USING [Option],
PrincOps USING [bytesPerPage, PageCount, wordsPerPage],
PrincOpsUtils USING [PagesForWords],
Process USING [Detach],
PSAssignedTypes USING [tBackup, tSpooled],
PSAsyncMsg USING [ExpandArrayAndPutString],
PSVolume USING [GetDefaultSession, GetDirectory],
QueueFile,
Rope USING [FromRefText, ROPE, ToRefText],
System USING [nullID, UniversalID],
XMessage USING [StringArray]
;
PrintQueueImpl: MONITOR
IMPORTS BasicTime, FS, NSString, PrincOpsUtils, Process, PSAsyncMsg, Rope
EXPORTS PrintQueue, QueueFile =
BEGIN
ERRORS
Error: PUBLIC ERROR [errortype: PrintQueue.Errortype] = CODE;
InsufficientPages: PUBLIC ERROR = CODE; -- for QueueFile
CreateError: PUBLIC ERROR = CODE; -- for QueueFile
LocalError: ERROR = CODE;
TYPES
ROPE: TYPE ~ Rope.ROPE;
ObjectStatus: TYPE = MACHINE DEPENDENT{pending(0), current(1), complete(2), scavanged(3), unlinked(4), null(maxObjectStatus)};
QueueElement: TYPE = RECORD [
qObject: PrintQueue.QueueObjectHandle ←,
previous, next: CARDINAL ←,
previous points to the front of the queue where the earlier entries are, next points toward the end of the queue where new entries are added.
stage: PrintQueue.QueueStage ←,
status: ObjectStatus ←,
testPattern: BOOLEANFALSE];
NotifyProcs: TYPE = ARRAY PrintQueue.QueueStage OF PrintQueue.NewObjectQueuedProc;
Queue: TYPE = ARRAY [0..builtInFixedQueueSize) OF QueueElement;
QueueObjectPointer: TYPE = REF QueueObjectArray;
QueueObjectArray: TYPE = ARRAY [0..builtInFixedQueueSize) OF PrintQueue.QueueObject;
DataRecord: TYPE = RECORD [queue: Queue, qStats: PrintQueue.StatsRecord];
Constants
builtInFixedQueueSize: CARDINAL = 40;
maxObjectStatus: CARDINAL = 7;
maxStringBytes: CARDINAL = PrintQueue.maxStringBytes;
nullQueueIndex: CARDINAL = LAST[CARDINAL];
blank: CHAR = ' ;
dataSpacePages: PrincOps.PageCount = PrincOpsUtils.PagesForWords[
SIZE[DataRecord] + SIZE[QueueObjectArray] + 1];
backingStoreFileType: NSFile.Type = PSAssignedTypes.tBackup;
tSpool: NSFile.Type = PSAssignedTypes.tSpooled;
Globals
printingOption: PrintingTypes.Option ← unknown;
initialized: BOOLEANFALSE; -- for safety
qFiletrace: QueueFile.TraceLevel ← none;
queueObjects: QueueObjectPointer; -- can be declared in Initialize's local frame
notifyProc: NotifyProcs ← ALL[NIL];
queueSpace: Space.Interval ← Space.nullInterval; -- to DataRecord in VM
dataRecord: REF DataRecord ← NIL;
spoolDirectory: ROPENIL; -- Is opened/created in the Initialize.
session: NSFile.Session = PSVolume.GetDefaultSession[];
==== ==== ==== ==== ==== ====
PrintQueue PROCEDURES
==== ==== ==== ==== ==== ====
Requeue: PUBLIC ENTRY PROCEDURE [
qOH: PrintQueue.QueueObjectHandle ← PrintQueue.nilQueueObjectHandle,
fromQueue: PrintQueue.QueueStage, toQueue: PrintQueue.QueueStage,
position: PrintQueue.RelativePosition ← back]
RETURNS
[returnQOH: PrintQueue.QueueObjectHandle] = BEGIN
ENABLE UNWIND => NULL;
If fromQueue Empty then return nilQueueObjectHandle else next QueueObjectHandle (i.e., from front of fromQueue) position specifies the relative positon of the toQueue;
Requeues to inactive queue means that the QueueObject's fid can be deleted.
qIndex: CARDINAL IF qOH # PrintQueue.nilQueueObjectHandle
THEN FindIndex[qOH] ELSE FindFirst[fromQueue];
SELECT TRUE FROM
qIndex = nullQueueIndex => RETURN [PrintQueue.nilQueueObjectHandle];
fromQueue = inactive => MakeNull[qIndex];
fromQueue = tpInactive => MakeTPNull[qIndex];
toQueue = inactive => BEGIN
IF dataRecord.queue[qIndex].testPattern THEN toQueue ← tpInactive;
DeleteFile[dataRecord.queue[qIndex].qObject]; --This used to be done only if qObject wasn't a test pattern; now done for all because test pattern interleaved files need to be deleted for feps9700 and DeleteFile will not succeed in deleting a test pattern interpress file.
END;
ENDCASE => NULL;
returnQOH ← SELECT TRUE FROM
qOH # PrintQueue.nilQueueObjectHandle AND dataRecord.queue[qIndex].next # nullQueueIndex => dataRecord.queue[dataRecord.queue[qIndex].next].qObject, --return next
qOH # PrintQueue.nilQueueObjectHandle => PrintQueue.nilQueueObjectHandle,
queue must be empty
ENDCASE => dataRecord.queue[qIndex].qObject; -- return first one
IF fromQueue # toQueue THEN BEGIN
Unlink[qIndex];
IF position = back THEN AddLast[qIndex, toQueue] ELSE AddFirst[qIndex, toQueue];
END;
Space.ForceOut[queueSpace];
InternalBackup[]; --Backup the print queue to the backing file.
TellClient[toQueue];
END; -- Requeue
Next: PUBLIC ENTRY PROCEDURE [
qOH: PrintQueue.QueueObjectHandle ← PrintQueue.nilQueueObjectHandle,
fromQueue: PrintQueue.QueueStage] RETURNS [PrintQueue.QueueObjectHandle] = BEGIN
ENABLE UNWIND => NULL;
If qOH=nilQueueObjectHandle then return the QueueObjectHandleat the front of the fromQueue (i.e. the oldest entry on the queue)
Else if there's a newer QueueObject on the queue than the specified QueueObject then return it's QueueObjectHandle
Else return nilQueueObjectHandle;
Queue entries are not removed from the queue and are not displaced in any way.
qIndex: CARDINAL IF qOH # PrintQueue.nilQueueObjectHandle
THEN FindIndex[qOH] ELSE FindFirst[fromQueue];
RETURN[SELECT TRUE FROM
qIndex = nullQueueIndex AND qOH = PrintQueue.nilQueueObjectHandle --queue must be empty
=> PrintQueue.nilQueueObjectHandle,
qIndex # nullQueueIndex AND qOH = PrintQueue.nilQueueObjectHandle --return first entry
=> dataRecord.queue[qIndex].qObject,
qIndex # nullQueueIndex AND dataRecord.queue[qIndex].next # nullQueueIndex
return next
=> dataRecord.queue[dataRecord.queue[qIndex].next].qObject,
ENDCASE => PrintQueue.nilQueueObjectHandle]; --no more in queue
END; -- Next
Previous: PUBLIC ENTRY PROCEDURE [
qOH: PrintQueue.QueueObjectHandle ← PrintQueue.nilQueueObjectHandle,
fromQueue: PrintQueue.QueueStage] RETURNS [PrintQueue.QueueObjectHandle] = BEGIN
ENABLE UNWIND => NULL;
If qOH=nilQueueObjectHandle then return the QueueObjectHandle at the back of the fromQueue (i.e. the newest entry on the queue)
Else if there's an older QueueObject on the queue than the specified QueueObject then return it's QueueObjectHandle
Else return nilQueueObjectHandle;
Queue entries are not removed from the queue and are not displaced in any way.
qIndex: CARDINAL IF qOH # PrintQueue.nilQueueObjectHandle
THEN FindIndex[qOH] ELSE FindLast[fromQueue];
RETURN[SELECT TRUE FROM
qIndex = nullQueueIndex AND qOH = PrintQueue.nilQueueObjectHandle --queue must be empty
=> PrintQueue.nilQueueObjectHandle,
qIndex # nullQueueIndex AND qOH = PrintQueue.nilQueueObjectHandle --return last entry
=> dataRecord.queue[qIndex].qObject,
qIndex # nullQueueIndex AND dataRecord.queue[qIndex].previous # nullQueueIndex
return previous
=> dataRecord.queue[dataRecord.queue[qIndex].previous].qObject,
ENDCASE => PrintQueue.nilQueueObjectHandle]; --no more in queue
END; -- Previous
Empty: PUBLIC ENTRY PROCEDURE [queue: PrintQueue.QueueStage] RETURNS [BOOLEAN] =
BEGIN ENABLE UNWIND => NULL; RETURN[FindFirst[queue] = nullQueueIndex]; END; -- Empty
ReserveForTestPattern: PUBLIC ENTRY PROCEDURE [
qOH: PrintQueue.QueueObjectHandle ← PrintQueue.nilQueueObjectHandle,
fromQueue: PrintQueue.QueueStage ← inactive,
toQueue: PrintQueue.QueueStage ← tpInactive]
RETURNS
[PrintQueue.QueueObjectHandle] = BEGIN
ENABLE UNWIND => NULL;
qIndex: CARDINAL IF qOH # PrintQueue.nilQueueObjectHandle
THEN FindIndex[qOH] ELSE FindFirst[fromQueue];
SELECT TRUE FROM
qIndex = nullQueueIndex => RETURN[PrintQueue.nilQueueObjectHandle];
fromQueue = inactive => MakeNull[qIndex];
ENDCASE => NULL;
dataRecord.queue[qIndex].testPattern ← TRUE;
Unlink[qIndex];
AddLast[qIndex, toQueue];
InternalBackup[];
Space.ForceOut[queueSpace];
TellClient[toQueue];
RETURN[dataRecord.queue[qIndex].qObject];
END; -- ReserveForTestPattern
ReleaseAsTestPattern: PUBLIC ENTRY PROCEDURE [qOH: PrintQueue.QueueObjectHandle,
fromQueue: PrintQueue.QueueStage ← tpInactive,
toQueue: PrintQueue.QueueStage ← inactive] = BEGIN
ENABLE UNWIND => NULL;
qIndex: CARDINAL IF qOH # PrintQueue.nilQueueObjectHandle
THEN FindIndex[qOH] ELSE FindFirst[fromQueue];
dataRecord.queue[qIndex].testPattern ← FALSE;
DeleteFile[dataRecord.queue[qIndex].qObject];
MakeNull[qIndex];
Unlink[qIndex];
AddLast[qIndex, toQueue];
InternalBackup[];
Space.ForceOut[queueSpace];
TellClient[toQueue];
END; -- ReleaseAsTestPattern
QueueWatcher: PUBLIC ENTRY PROCEDURE [
proc: PrintQueue.NewObjectQueuedProc, queueStage: PrintQueue.QueueStage] = BEGIN
ENABLE UNWIND => NULL;
IF proc # NIL AND notifyProc[queueStage] # NIL THEN Error[multipleWatchers];
notifyProc[queueStage] ← proc;
IF FindFirst[queueStage] # nullQueueIndex THEN TellClient[queueStage];
END; -- QueueWatcher
LocateHandle: PUBLIC ENTRY PROCEDURE [fileSent: System.UniversalID]
RETURNS
[qOH: PrintQueue.QueueObjectHandle ← PrintQueue.nilQueueObjectHandle,
queueStage: PrintQueue.QueueStage ← inactive] = BEGIN
ENABLE UNWIND => NULL;
FOR qIndex: CARDINAL IN [0..builtInFixedQueueSize) DO
IF dataRecord.queue[qIndex].qObject.uid = fileSent THEN
RETURN[dataRecord.queue[qIndex].qObject, dataRecord.queue[qIndex].stage];
ENDLOOP;
END; -- LocateHandle
WhereIs: PUBLIC ENTRY PROCEDURE [qOH: PrintQueue.QueueObjectHandle]
RETURNS
[valid: BOOLEAN, queueStage: PrintQueue.QueueStage] = BEGIN
ENABLE UNWIND => NULL;
FOR qIndex: CARDINAL IN [0..builtInFixedQueueSize) DO
IF dataRecord.queue[qIndex].qObject = qOH THEN
RETURN[TRUE, dataRecord.queue[qIndex].stage];
ENDLOOP;
RETURN[FALSE, temp --qOH not actually found--];
END; -- WhereIs
CopyStringIn: PUBLIC ENTRY PROCEDURE [qOH: PrintQueue.QueueObjectHandle,
fromString: NSString.String, toField: PrintQueue.ObjectStringField] = BEGIN -- Copies fromString to qOH.toField
ENABLE UNWIND => NULL;
truncatedString: NSString.String;
toString: NSString.String;
IF qOH = PrintQueue.nilQueueObjectHandle THEN Error[nullQueueObject];
toString ← SELECT toField FROM
fileName => qOH.fileName,
localFileName => qOH.localFileName,
sender => qOH.sender,
recipient => qOH.recipient,
operatorMsg => qOH.operatorMsg,
ENDCASE --errorMsg-- => qOH.errorMsg;
truncatedString ← NSString.TruncateString[fromString,
IF fromString.length < toString.maxLength
THEN fromString.length ELSE toString.maxLength];
toString.length ← 0;
toString ← NSString.AppendString[toString, truncatedString];
END; -- CopyStringIn
GetQueueObject: PUBLIC ENTRY PROCEDURE []
RETURNS
[qOH: PrintQueue.QueueObjectHandle] = BEGIN
NOTE: The queue object must always be allocated with the largest variant type, so the client can set the queue object to any variant he wants to.
ENABLE UNWIND => NULL;
qOH ← NEW[PrintQueue.QueueObject.fax495];
END; -- of GetQueueObject
CopyQueueObject: PUBLIC ENTRY PROCEDURE [
fromQOH: PrintQueue.QueueObjectHandle, toQOH: PrintQueue.QueueObjectHandle] =
TRUSTED BEGIN
ENABLE UNWIND => NULL;
Copy: PROC [p: PACKED ARRAY PrintQueue.StringBody OF CHAR]
RETURNS [s: NSString.String] = TRUSTED BEGIN
s ← NEW[NSString.StringRep[LAST[PrintQueue.StringBody]+1]];
FOR i: PrintQueue.StringBody IN PrintQueue.StringBody DO
s[i] ← p[i];
ENDLOOP;
END;
CopyPhone: PROC [p: PACKED ARRAY [0..PrintQueue.maxPhoneNoLength) OF CHAR]
RETURNS [s: NSString.String] = TRUSTED BEGIN
s ← NEW[NSString.StringRep[PrintQueue.maxPhoneNoLength]];
FOR i: [0..PrintQueue.maxPhoneNoLength) IN [0..PrintQueue.maxPhoneNoLength) DO
s[i] ← p[i];
ENDLOOP;
END;
Copy4: PROC [p: PACKED ARRAY [0..4) OF CHAR] RETURNS [s: NSString.String] = TRUSTED BEGIN
s ← NEW[NSString.StringRep[4]];
FOR i: [0..4) IN [0..4) DO s[i] ← p[i]; ENDLOOP;
END;
toQOH^ ← fromQOH^;
Reset toQOH string pointers correctly:
toQOH.fileName ← Copy[toQOH.fileNameBody];
toQOH.localFileName ← Copy[toQOH.localFileNameBody];
toQOH.sender ← Copy[toQOH.senderBody];
toQOH.recipient ← Copy[toQOH.recipientBody];
toQOH.operatorMsg ← Copy[toQOH.operatorMsgBody];
toQOH.errorMsg ← Copy[toQOH.errorMsgBody];
WITH q: toQOH SELECT FROM
fax495 => FOR i: CARDINAL IN [0..PrintQueue.maxFaxPhoneNos) DO
q.transmitData[i].phoneNumber ← CopyPhone[q.transmitData[i].phoneNumberBody];
q.transmitData[i].errorCode ← Copy4[q.transmitData[i].errorCodeBody];
ENDLOOP;
ENDCASE;
END; --CopyQueueObject
Stats: PUBLIC ENTRY PROCEDURE RETURNS [stats: PrintQueue.StatsRecord] =
BEGIN ENABLE UNWIND => NULL; RETURN[dataRecord.qStats]; END; -- Stats
ResetStats: PUBLIC ENTRY PROCEDURE = BEGIN
dataRecord.qStats ← [statsFrom: BasicTime.Now[], inconsistantAtInit: 0, activity: ALL[0]];
END; -- ResetStats
Backup: PUBLIC ENTRY PROCEDURE = BEGIN
Copies print queue to backing file. (Used in FaxMarkerControlImpl to save information about local print/phone number completions, since Requeue isn't called until the entire job finishes marking.)
InternalBackup[];
Space.ForceOut[queueSpace];
END; --Backup
==== ==== ==== ==== ==== ====
QueueFile PROCEDURES
==== ==== ==== ==== ==== ====
CreateTempFile: PUBLIC ENTRY PROCEDURE [fName: NSString.String, fBytes: LONG CARDINAL]
RETURNS [fileID: FS.OpenFile, fileH: IO.STREAM] =
BEGIN ENABLE UNWIND => NULL;
trucatedString: NSString.String;
selection: NSFile.Selections ← NSFile.noSelections;
attribute: NSFile.Attributes ← NSFile.GetAttributesRecord[];
=====================================
CreateFile: PROC = BEGIN
=====================================
attrList: ARRAY [0..3) OF NSFile.Attribute ← [
[type[tSpool]], [name[trucatedString]], [dataSize[fBytes]]];
fileID ← FS.Create[name: Rope.FromRefText[trucatedString],
pages: (fBytes/PrincOps.bytesPerPage)+1];
END; -- CreateFile
=====================================
trucatedString ← NSString.TruncateString[fName, FS.maxFNameLength];
fileID ← FS.nullOpenFile;
CreateFile[ ! FS.Error => BEGIN
IF qFiletrace # none THEN BEGIN
nsCreateFileError: NSString.String = " **CreateFile: <1> !<2>";
stringArray: REF XMessage.StringArray ← NEW[XMessage.StringArray[2]];
stringArray.data[0] ← trucatedString;
stringArray.data[1] ← Rope.ToRefText[error.explanation];
PSAsyncMsg.ExpandArrayAndPutString[nsCreateFileError, stringArray];
END;
IF error.code = $volumeFull THEN ERROR InsufficientPages ELSE ERROR CreateError;
END;
];
selection.interpreted[fileID] ← TRUE;
NSFile.GetAttributes[fileH, selection, attribute, session
! NSFile.Error => BEGIN
IF qFiletrace # none THEN BEGIN
nsCreateFileError: NSString.String = Str[" **CreateFile: <1>; <2>"L];
stringArray: ARRAY [0..2) OF NSString.String;
stringArray[0] ← trucatedString;
[stringArray[1],] ← MsgOps.FilingErrToMsg[error];
PSAsyncMsg.ExpandArrayAndPutString[nsCreateFileError, DESCRIPTOR[stringArray]];
END;
GOTO Error;
END;
];
fileID ← attribute.fileID;
NSFile.ReleaseAttributesRecord[attribute];
EXITS Error => BEGIN
IF fileH # NSFile.nullHandle THEN NSFile.Close[fileH, session
! NSFile.Error => CONTINUE];
ERROR CreateError;
END;
END; -- CreateTempFile
Delete: PUBLIC ENTRY PROCEDURE [fileID: FS.OpenFile, file: IO.STREAMNIL] RETURNS [FS.OpenFile] = BEGIN
ENABLE UNWIND => NULL;
IF fileID # FS.nullOpenFile THEN FS.Close[fileID ! FS.Error => CONTINUE]; --cause FileDelete Opens it again.
FileDelete[fileID ! LocalError => CONTINUE];
RETURN [FS.nullOpenFile];
END; -- Delete
MakePermanent: PUBLIC PROCEDURE [file: FS.OpenFile, attrs: NSString.String] = BEGIN
NSFile.Move[file, spoolDirectory, attrs, session];
[] ← FS.Copy[from: FS.GetName[file].fullFName, to: Rope.FromRefText[attrs], wDir: spoolDirectory]
END; -- MakePermanent.
SetTrace: PUBLIC PROCEDURE [trace: QueueFile.TraceLevel] = {qFiletrace ← trace};
==== ==== ==== ==== ==== ====
INTERNAL PROCEDURES
==== ==== ==== ==== ==== ====
InternalBackup: INTERNAL PROCEDURE [] = BEGIN
Space.ForceOut[];
END;
FindIndex: INTERNAL PROCEDURE [qOH: PrintQueue.QueueObjectHandle]
RETURNS
[qIndex: CARDINAL] = BEGIN
FOR qIndex IN [0..builtInFixedQueueSize) DO
IF dataRecord.queue[qIndex].qObject = qOH THEN RETURN[qIndex]; ENDLOOP;
ERROR Error[invalidQueueObject];
END; -- FindIndex
MakeNull: INTERNAL PROCEDURE [qIndex: CARDINAL] = BEGIN
Copy: PROC [p: PACKED ARRAY PrintQueue.StringBody OF CHAR]
RETURNS [s: NSString.String] = BEGIN
s ← NEW[NSString.StringRep[LAST[PrintQueue.StringBody]+1]];
FOR i: PrintQueue.StringBody IN PrintQueue.StringBody DO
s[i] ← p[i];
ENDLOOP;
END;
qOH: PrintQueue.QueueObjectHandle ← dataRecord.queue[qIndex].qObject;
IF dataRecord.queue[qIndex].status # null THEN BEGIN
qOH^ ← [
fileName: NEW[NSString.StringRep[maxStringBytes]],
localFileName: NEW[NSString.StringRep[maxStringBytes]],
sender: NEW[NSString.StringRep[maxStringBytes]],
recipient: NEW[NSString.StringRep[maxStringBytes]],
operatorMsg: NEW[NSString.StringRep[maxStringBytes]],
errorMsg: NEW[NSString.StringRep[maxStringBytes]],
optionVariant: unknown[] --Compiler won't accept select stmt (below) here. **AJ 12/20/83
];
dataRecord.queue[qIndex].status ← null;
END;
IF qOH.option # printingOption THEN BEGIN --Change printing option if necessary.
SELECT printingOption FROM
bansheeDl => qOH.optionVariant ← bansheeDl[];
d1 => qOH.optionVariant ← d1[];
fax295 => qOH.optionVariant ← fax295[];
fax495 => qOH.optionVariant ← fax495[];
feps9700 => qOH.optionVariant ← feps9700[];
fx3500 => qOH.optionVariant ← fx3500[];
raven => qOH.optionVariant ← raven[];
ENDCASE => ERROR;
WITH q: qOH SELECT FROM
fax495 => FOR i: CARDINAL IN [0..PrintQueue.maxFaxPhoneNos) DO
q.transmitData[i].phoneNumber ←
NEW
[NSString.StringRep[PrintQueue.maxPhoneNoLength]];
q.transmitData[i].errorCode ← NEW[NSString.StringRep[4]];
ENDLOOP;
ENDCASE;
END;
END; -- MakeNull
MakeTPNull: INTERNAL PROCEDURE [qIndex: CARDINAL] = BEGIN -- assumes STRINGs are still pointing at correct bodies
qOH: PrintQueue.QueueObjectHandle ← dataRecord.queue[qIndex].qObject;
IF dataRecord.queue[qIndex].status # null THEN BEGIN
qOH.fileID ← FS.nullOpenFile;
qOH.uid ← System.nullID;
qOH.numberCopies ← qOH.platesDecomposed ← 0;
qOH.firstPageToPrint ← 1;
qOH.lastPageToPrint ← LAST[CARDINAL];
qOH.twoSided ← qOH.staple ← FALSE;
qOH.printObjectHandle ← PrintQueue.nilPrintObjectHandle;
qOH.currentStatus ← qOH.priorStatus ← null;
qOH.bannerOnly ← FALSE;
qOH.sender.length ← qOH.recipient.length ← qOH.operatorMsg.length
← qOH.errorMsg.length ← 0;
dataRecord.queue[qIndex].status ← null;
END;
SELECT printingOption FROM --Even if queue object was already null, must initialize it to current printing option.
bansheeDl => qOH.optionVariant ← bansheeDl[];
d1 => qOH.optionVariant ← d1[];
fax295 => qOH.optionVariant ← fax295[];
fax495 => qOH.optionVariant ← fax495[];
feps9700 => qOH.optionVariant ← feps9700[];
fx3500 => qOH.optionVariant ← fx3500[];
raven => qOH.optionVariant ← raven[];
ENDCASE => ERROR;
WITH q: qOH SELECT FROM
fax495 => FOR i: CARDINAL IN [0..PrintQueue.maxFaxPhoneNos) DO
q.transmitData[i].phoneNumber ← NEW[NSString.StringRep[PrintQueue.maxPhoneNoLength]];
q.transmitData[i].errorCode ← NEW[NSString.StringRep[4]];
ENDLOOP;
ENDCASE;
END; -- MakeTPNull
DeleteFile: INTERNAL PROCEDURE [qOH: PrintQueue.QueueObjectHandle] = BEGIN
IF qOH.deleteInactiveFile AND qOH.fileID # FS.nullOpenFile THEN
FileDelete[qOH.fileID ! LocalError => CONTINUE];
qOH.localFileName.length ← 0;
qOH.fileID ← FS.nullOpenFile;
WITH q: qOH SELECT FROM
feps9700 => BEGIN --delete interleaved IP master, if any
IF q.ivFileID # FS.nullOpenFile THEN FileDelete[q.ivFileID ! LocalError => CONTINUE];
q.ivFileID ← FS.nullOpenFile;
END;
ENDCASE;
END; -- DeleteFile
FileDelete: INTERNAL PROCEDURE [fileID: FS.OpenFile] = BEGIN
file: FS.OpenFile;
attrList: ARRAY [0..1) OF NSFile.Attribute ← [[fileID[fileID]]];
fileError: BOOLEANFALSE;
name: ROPEFS.GetName[fileID].fullFName;
FS.Close[fileID];
FS.Delete[name: name]
file ← NSFile.Open[
attributes: DESCRIPTOR[attrList],
directory: spoolDirectory, session: session !
NSFile.Error => BEGIN
IF qFiletrace # none THEN BEGIN
nsFileDeleteError: NSString.String = Str[" **FileDelete: Open !<1>"L];
stringArray: ARRAY [0..1) OF NSString.String;
[stringArray[0],] ← MsgOps.FilingErrToMsg[error];
PSAsyncMsg.ExpandArrayAndPutString[nsFileDeleteError, DESCRIPTOR[stringArray]];
END;
GOTO Error;
END];
NSFile.Delete[file, session !
NSFile.Error => BEGIN
IF qFiletrace # none THEN BEGIN
nsFileDeleteError: NSString.String = Str[" **FileDelete: Delete !<1>"L];
stringArray: ARRAY [0..1) OF NSString.String;
[stringArray[0],] ← MsgOps.FilingErrToMsg[error];
PSAsyncMsg.ExpandArrayAndPutString[nsFileDeleteError, DESCRIPTOR[stringArray]];
END;
GOTO Error;
END];
EXITS Error => LocalError;
END; -- FileDelete
Unlink: INTERNAL PROCEDURE [qIndex: CARDINAL] = BEGIN OPEN dataRecord;
IF queue[qIndex].next # nullQueueIndex THEN
queue[queue[qIndex].next].previous ← queue[qIndex].previous;
IF queue[qIndex].previous # nullQueueIndex THEN
queue[queue[qIndex].previous].next ← queue[qIndex].next;
queue[qIndex].previous ← queue[qIndex].next ← nullQueueIndex;
queue[qIndex].status ← unlinked;
END; -- Unlink
AddLast: INTERNAL PROCEDURE [qIndex: CARDINAL, qStage: PrintQueue.QueueStage,
objStatus: ObjectStatus ← pending] = BEGIN OPEN dataRecord; -- assumes queue[qIndex] has been Unlinked.
IF queue[qIndex].status # unlinked THEN ERROR; -- removable for production
queue[qIndex].previous ← FindLast[qStage];
IF queue[qIndex].previous # nullQueueIndex THEN
queue[queue[qIndex].previous].next ← qIndex;
queue[qIndex].stage ← qStage;
queue[qIndex].status ← objStatus;
queue[qIndex].qObject.timeStamp[qStage] ← BasicTime.Now[];
qStats.activity[qStage] ← qStats.activity[qStage] + 1;
END; -- AddLast
AddFirst: INTERNAL PROCEDURE [qIndex: CARDINAL, qStage: PrintQueue.QueueStage,
objStatus: ObjectStatus ← pending] = BEGIN OPEN dataRecord; -- assumes queue[qIndex] has been Unlinked.
IF queue[qIndex].status # unlinked THEN ERROR; -- removable for production
queue[qIndex].next ← FindFirst[qStage];
IF queue[qIndex].next # nullQueueIndex THEN queue[queue[qIndex].next].previous ← qIndex;
queue[qIndex].stage ← qStage;
queue[qIndex].status ← objStatus;
queue[qIndex].qObject.timeStamp[qStage] ← BasicTime.Now[];
qStats.activity[qStage] ← qStats.activity[qStage] + 1;
END; -- AddFirst
FindLast: INTERNAL PROCEDURE [qStage: PrintQueue.QueueStage]
RETURNS
[qIndex: CARDINAL] =
BEGIN OPEN dataRecord; -- returns qIndex of last (latest) entry in queue or nullQueueIndex
FOR qIndex IN [0..builtInFixedQueueSize) DO
IF queue[qIndex].stage = qStage THEN BEGIN
UNTIL queue[qIndex].next = nullQueueIndex DO qIndex ← queue[qIndex].next; ENDLOOP;
RETURN[qIndex];
END;
ENDLOOP;
RETURN[nullQueueIndex];
END; -- FindLast
FindFirst: INTERNAL PROCEDURE [qStage: PrintQueue.QueueStage]
RETURNS
[qIndex: CARDINAL] =
BEGIN OPEN dataRecord; -- returns qIndex of first (earliest) entry in queue or nullQueueIndex
FOR qIndex IN [0..builtInFixedQueueSize) DO
IF queue[qIndex].stage = qStage THEN BEGIN
UNTIL queue[qIndex].previous = nullQueueIndex DO
qIndex ← queue[qIndex].previous; ENDLOOP;
RETURN[qIndex];
END;
ENDLOOP;
RETURN[nullQueueIndex];
END; -- FindFirst
TellClient: INTERNAL PROCEDURE [q: PrintQueue.QueueStage] =
TRUSTED BEGIN IF notifyProc[q] # NIL THEN Process.Detach[FORK notifyProc[q]]; END;
==== ==== ==== ====
INITIALIZATION
==== ==== ==== ====
Initialize: PUBLIC PROCEDURE [directory: REF TEXT, queueEntries: CARDINAL,
currentOption: PrintingTypes.Option] RETURNS [queueSize: CARDINAL] = BEGIN
nsQueueBackingStore: ROPE = "PrintQueue14.BackingStore";
oldnsQueueBackingStore: ROPE = "PrintQueue10.BackingStore";
file: FS.OpenFile ← FS.nullOpenFile;
newBackingFile: BOOLEANFALSE;
====================================================
CreateBackingFile: PROC[] = BEGIN
====================================================
attrList: ARRAY [0..3) OF NSFile.Attribute ← [
[type[backingStoreFileType]], [dataSize[dataSpaceBytes]],
[name[nsQueueBackingStore]]];
file ← FS.Create[name: nsQueueBackingStore, wDir: Rope.FromRefText[directory],
pages: dataSpacePages];
END; -- CreateBackingFile
====================================================
InitializeSpace: PROC = BEGIN OPEN dataRecord;
====================================================
FOR i: CARDINAL IN [0..builtInFixedQueueSize) DO -- Allocate the QueueObjects
queue[i] ← [qObject: @queueObjects[i], previous: nullQueueIndex,
next: nullQueueIndex, stage: inactive, status: null];
queue[i].qObject­ ← [
fileName: [
bytes: LOOPHOLE[@queue[i].qObject.fileNameBody], length: 0,
maxlength: maxStringBytes],
localFileName: [
bytes: LOOPHOLE[@queue[i].qObject.localFileNameBody], length: 0,
maxlength: maxStringBytes],
sender: [
bytes: LOOPHOLE[@queue[i].qObject.senderBody], length: 0,
maxlength: maxStringBytes],
recipient: [
bytes: LOOPHOLE[@queue[i].qObject.recipientBody], length: 0,
maxlength: maxStringBytes],
operatorMsg: [
bytes: LOOPHOLE[@queue[i].qObject.operatorMsgBody], length: 0,
maxlength: maxStringBytes],
errorMsg: [
bytes: LOOPHOLE[@queue[i].qObject.errorMsgBody], length: 0,
maxlength: maxStringBytes],
optionVariant: unknown[] --Compiler won't accept select stmt (below) here. **AJ 12/20/83
];
SELECT currentOption FROM
bansheeDl => queue[i].qObject.optionVariant ← bansheeDl[];
d1 => queue[i].qObject.optionVariant ← d1[];
fax295 => queue[i].qObject.optionVariant ← fax295[];
fax495 => queue[i].qObject.optionVariant ← fax495[];
feps9700 => queue[i].qObject.optionVariant ← feps9700[];
fx3500 => queue[i].qObject.optionVariant ← fx3500[];
raven => queue[i].qObject.optionVariant ← raven[];
ENDCASE => ERROR;
WITH q: queue[i].qObject SELECT FROM
fax495 => FOR j: CARDINAL IN [0..PrintQueue.maxFaxPhoneNos) DO
q.transmitData[j].phoneNumber ← [
bytes: LOOPHOLE[@q.transmitData[j].phoneNumberBody],
length: 0, maxlength: PrintQueue.maxPhoneNoLength];
q.transmitData[j].errorCode ← [
bytes: LOOPHOLE[@q.transmitData[j].errorCodeBody],
length: 0, maxlength: 4];
ENDLOOP;
ENDCASE;
ENDLOOP;
FOR i: CARDINAL IN [1..builtInFixedQueueSize) DO -- backward and forward indexes
queue[i].previous ← i - 1; queue[i - 1].next ← i; ENDLOOP;
qStats ← [statsFrom: BasicTime.Now[], inconsistantAtInit: 0, activity: ALL[0]];
Space.ForceOut[queueSpace];
END; -- InitializeSpace
====================================================
FixSpace: PROC = BEGIN ERROR; END; -- FixSpace
====================================================
CheckData: PROC = BEGIN OPEN dataRecord;
FOR i: CARDINAL IN [0..builtInFixedQueueSize) DO -- check the queueObjects' files
queue[i].qObject ← @queueObjects[i];
ensure that strings are pointing at correct location
queue[i].qObject.fileName.bytes ← LOOPHOLE[@queue[i].qObject.fileNameBody];
queue[i].qObject.localFileName.bytes ← LOOPHOLE[@queue[i].qObject.localFileNameBody];
queue[i].qObject.sender.bytes ← LOOPHOLE[@queue[i].qObject.senderBody];
queue[i].qObject.recipient.bytes ← LOOPHOLE[@queue[i].qObject.recipientBody];
queue[i].qObject.operatorMsg.bytes ← LOOPHOLE[@queue[i].qObject.operatorMsgBody];
queue[i].qObject.errorMsg.bytes ← LOOPHOLE[@queue[i].qObject.errorMsgBody];
WITH q: queue[i].qObject SELECT FROM
fax495 => FOR j: CARDINAL IN [0..PrintQueue.maxFaxPhoneNos) DO
q.transmitData[j].phoneNumber.bytes ← LOOPHOLE[@q.transmitData[j].phoneNumberBody];
q.transmitData[j].errorCode.bytes ← LOOPHOLE[@q.transmitData[j].errorCodeBody];
ENDLOOP;
feps9700 =>
Check for interleaved interpress master file:
IF q.ivFileID # NSFile.nullID THEN BEGIN
fH: NSFile.Handle ← NSFile.OpenByReference[
reference: NSFile.MakeReference[q.ivFileID], session: session !
NSFile.Error => GOTO MergedFileNotFound];
NSFile.Close[file: fH, session: session];
EXITS
MergedFileNotFound =>
Only docs on merged and forwarding queues should have
permanent interleaved files. If permanent files
are not found for those docs, something is wrong.
IF queue[i].stage = merged OR queue[i].stage = forwarding THEN BEGIN
q.ivFileID ← NSFile.nullID; -- ??????
qStats.inconsistantAtInit ← qStats.inconsistantAtInit + 1;
[] ← Requeue[queue[i].qObject, queue[i].stage, inactive];
END
ELSE q.ivFileID ← NSFile.nullID;
END;
ENDCASE;
queue[i].qObject.printObjectHandle ← NIL; --Bands List and Font Load were temp files
Check for spooled file:
IF queue[i].qObject.fileID # NSFile.nullID THEN BEGIN
fH: NSFile.Handle ← NSFile.OpenByReference[
reference: NSFile.MakeReference[queue[i].qObject.fileID], session: session !
NSFile.Error => GOTO FileNotFound];
NSFile.Close[file: fH, session: session];
EXITS
FileNotFound => BEGIN
queue[i].testPattern ← FALSE;
queue[i].qObject.fileID ← NSFile.nullID; -- ??????
qStats.inconsistantAtInit ← qStats.inconsistantAtInit + 1;
[] ← Requeue[queue[i].qObject, queue[i].stage, inactive];
END;
END
ELSE -- queue[i].qObject.fileID = NSFile.nullID
IF (queue[i].qObject.localFileName.length # 0
OR queue[i].stage # inactive)
AND (queue[i].stage # merged --spooled file was deleted for
AND queue[i].stage # forwarding --feps9700 merged files
AND queue[i].stage # forwarded) THEN BEGIN
queue[i].testPattern ← FALSE;
qStats.inconsistantAtInit ← qStats.inconsistantAtInit + 1;
[] ← Requeue[queue[i].qObject, queue[i].stage, inactive];
END;
ENDLOOP;
Space.ForceOut[queueSpace];
END; -- CheckData
====================================================
printingOption ← currentOption; --printingOption is a global var
IF initialized THEN ERROR;
initialized ← TRUE;
FS.Delete[name: oldnsQueueBackingStore, wDir: Rope.FromRefText[directory] ! FS.Error => CONTINUE]; -- delete old one
BEGIN --Open new print queue backing file.
file ← FS.Open[name: nsQueueBackingStore, wDir: Rope.FromRefText[directory]
! FS.Error => GOTO FileNotFound];
EXITS FileNotFound => {CreateBackingFile[]; newBackingFile ← TRUE; };
END;
BEGIN -- Create space for queue data and map it to backing file.
dataRecord ← NEW[DataRecord];
queueSpace ← NSSegment.Map[
origin: origin, access: NSFile.fullAccess, swapUnits: [uniform[3]], session: session];
3 page swap units because QueueObject is between 2 and 3 pages big
dataRecord ← queueSpace.pointer;
queueObjects ← LOOPHOLE[dataRecord + SIZE[DataRecord], LONG POINTER];
END; -- Create space
IF newBackingFile THEN InitializeSpace[] ELSE CheckData[];
spoolDirectory ← PSVolume.GetDirectory[tSpool];
RETURN [builtInFixedQueueSize];
END; -- Initialize
Str: PROCEDURE [s: REF TEXT] RETURNS [ns: NSString.String] = INLINE {
RETURN[NSString.StringFromMesaString[s]]};
END.
LOG
****EARLIER LOG ENTRIES DELETED. See archived version from 8.0.
17-Sep-84 16:16:12 - Jacks - Updated to new PrintQueue interface for 9.0; added provisions for bansheeDl, fax295 and feps9700/got rid of aps5.
9-Nov-84 11:39:53 - Jacks - DeleteFile now deletes interleaved ip master as well as spool file; added feps9700 stuff to CheckData.
21-Nov-84 14:16:47 - Jacks - When toQueue=inactive, Requeue now calls DeleteFile even if qObject is for test pattern.
17-Jun-85 15:44:28 - Jacks - Added copyright notice; updated to new PS Euclid interfaces.
28-Jun-85 10:37:28 - Jacks - Added d1 variants and went to new backing store; fixed sender/recipient bug in CheckData.
1-Jul-85 16:32:45 - Jacks - New backing store.
23-Jul-85 14:00:43 - Jacks - Updated to Euclid Service interfaces; got rid of QueueData in the DataRecord; deleted GetPages, SetMaxPages and GetDirectory.
13-Aug-85 9:54:54 - Jacks - Initialize firstPageToPrint to 1 instead of 0 in MakeTPNull.
26-Aug-85 10:22:49 - Jacks - Made use of NSFile.GetAttributesRecord in CreateTempFile to reduce local frame size.
9-Sep-85 15:28:12 - Jacks - Added GetQueueObject.
14-Oct-85 17:22:10 - Jacks - Added code for fax495 errorCode.