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
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: BOOLEAN ← FALSE];
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];
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: BOOLEAN ← FALSE; -- 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: ROPE ← NIL; -- 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.
STREAM ←
NIL]
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: BOOLEAN ← FALSE;
name: ROPE ← FS.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: BOOLEAN ← FALSE;
====================================================
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]]};
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.