-- file: IntPress.Mesa
-- edited by Brotz, November 13, 1981 12:24 PM
-- edited by Schroeder, 28-Oct-80 11:17:06

DIRECTORY
Ascii USING [SP],
csD: FROM "CoreStreamDefs" USING [Destroy, GetLength, GetPosition, Open,
OpenFromName, Position, ReadStream, Reset, SetPosition, StreamCopy, StreamHandle,
Write, WriteBlock],
DMSTimeDefs USING [currentTime, MapPackedTimeToTimeZoneString, PackedTime],
EFTPDefs USING [EFTPAbortSending, EFTPFinishSending, EFTPOpenForSending,
eftpReceiverBusyAbort, EFTPSendBlock, EFTPTimeOut, EFTPTroubleSending],
exD: FROM "ExceptionDefs" USING [AppendExceptionString, cancelHardcopy,
cannotConnectToPrinter, cantFindPrinter, DisplayBothExceptionLines,
DisplayExceptionStringOnLine, GetExceptionString, hardcopyCanceled, nil,
printerBusy, printerNotRespond, printerTimeout, printerTrouble, proceeding,
transmissionTo],
Inline USING [DIVMOD, LowHalf],
intCommon USING [fontDirectorySegment, hardCopies, hardcopyHost, hardcopyUserName,
passwordPrinting, twoSidedPrinting, user],
LaurelHardcopyDefs USING [aborted, AbortHardcopy, Byte, CheckForAbort,
DocumentDirectoryHeader, EntityTrailer, fontCom, FontDirectory, FontDirectoryRec,
FontNumber, inch, LineSegmentTable, magicNonPrintingWidth, Mica,
PartDirectoryEntry, setSpaceXCom, setXCom, setYCom, showCharactersCom,
showCharactersShortCom, widthTable],
lsD: FROM "LaurelStateDefs" USING [ReleaseStateSegment, SwapInStateSegment],
PupDefs USING [GetPupAddress, PupAddress, PupNameTrouble],
PupTypes USING [eftpReceiveSoc],
Stream USING [Block],
String USING [AppendChar, AppendString, StringBoundsFault, WordsForString],
TimeDefs USING [CurrentDayTime];

IntPress: PROGRAM
IMPORTS csD, DMSTimeDefs, EFTPDefs, exD, Inline, intC: intCommon,
LaurelHardcopyDefs, lsD, PupDefs, String, TimeDefs
EXPORTS LaurelHardcopyDefs =

BEGIN
OPEN LaurelHardcopyDefs;

nPressFilePages: CARDINAL = 240;

pressStream: csD.StreamHandle;
partDirectoryStream: csD.StreamHandle;
entityListStream: csD.StreamHandle;

dlStartPosition: csD.Position;

who: PupDefs.PupAddress;


WriteCommandAndMica: PROCEDURE [com: Byte, val: Mica] =
BEGIN
q, r: CARDINAL;
[q, r] ← Inline.DIVMOD[val, 400B];
WriteEntityByte[com];
WriteEntityByte[q];
WriteEntityByte[r];
END; -- of WriteCommandAndMica --


WriteEntityByte: PROCEDURE [val: Byte] = INLINE {csD.Write[entityListStream, val]};


WritePressByte: PROCEDURE [val: Byte] = INLINE {csD.Write[pressStream, val]};


WritePressString: PROCEDURE [string: STRING, nChars: CARDINAL] =
BEGIN
length: CARDINAL = MIN[string.length, nChars];
i: CARDINAL;
WritePressByte[length];
FOR i IN [0 .. length) DO WritePressByte[LOOPHOLE[string[i]]]; ENDLOOP;
THROUGH [length .. nChars) DO WritePressByte[0]; ENDLOOP;
END; -- of WritePressString --


AdvancePressStreamToNextPage: PROCEDURE RETURNS [incr: CARDINAL] =
BEGIN
p: csD.Position = csD.GetPosition[pressStream];
incr ← ((512 - Inline.LowHalf[p MOD 512]) MOD 512);
csD.SetPosition[pressStream, p+incr];
END; --of AdvancPressStreamToNextPage--


InitPressPage: PUBLIC PROCEDURE =
-- Initializes state variables.
BEGIN
dlStartPosition ← csD.GetPosition[pressStream];
END; -- of InitPressPage --


FinishPressPage: PUBLIC PROCEDURE =
-- Completes the current page part by pushing out the entity list onto the pressStream and
-- by adding a new entry to the part directory.
BEGIN
partDirectoryEntry: PartDirectoryEntry;
entityTrailer: EntityTrailer ← EntityTrailer
[entityType: 0,
fontSet: 0,
beginByteHigh: 0,
beginByteLow: 0,
byteLengthHigh: 0,
byteLengthLow: Inline.LowHalf[csD.GetPosition[pressStream] - dlStartPosition],
ye: 0,
xe: 0,
left: 0,
bottom: 0,
width: 8 * inch + inch/2,
height: 11 * inch,
entityLength: Inline.LowHalf[(csD.GetPosition[entityListStream] + 1) / 2 + SIZE[EntityTrailer]]];
IF csD.GetPosition[pressStream] MOD 2 = 1 THEN WritePressByte[377B];
WritePressByte[0];
WritePressByte[0];
IF csD.GetPosition[entityListStream] MOD 2 = 1 THEN WriteEntityByte[377B];
csD.SetPosition[entityListStream,0];
csD.StreamCopy[entityListStream, pressStream, csD.GetLength[entityListStream]];
csD.WriteBlock[pressStream, LOOPHOLE[@entityTrailer], 0, 2*SIZE[EntityTrailer]];
csD.Reset[entityListStream];

partDirectoryEntry ← PartDirectoryEntry
[partType: 0,
recordStart: Inline.LowHalf[dlStartPosition/512],
nRecords: 0, -- fill in later --
entityListPadding: AdvancePressStreamToNextPage[]/2];
partDirectoryEntry.nRecords ← Inline.LowHalf[(csD.GetPosition[pressStream] - dlStartPosition)/512];
csD.WriteBlock
[partDirectoryStream, LOOPHOLE[@partDirectoryEntry], 0, SIZE[PartDirectoryEntry]];
END; -- of FinishPressPage --


FinishPressFile: PUBLIC PROCEDURE [chunk: CARDINAL] =
-- Completes the press file by pushing out the font directory, the part directory, and the
-- document directory.
BEGIN
partDirStartPosition: csD.Position;
pressFileName: STRING ← "Mail, part x"L;
userNameString: STRING ← [31];
timeString: STRING ← [39];
-- don’t change this limit!
i: CARDINAL;
pt: DMSTimeDefs.PackedTime;
fontDirectory: FontDirectory;
documentDirectoryHeader: DocumentDirectoryHeader;

partDirectoryEntry: PartDirectoryEntry ← PartDirectoryEntry
[partType: 1,
recordStart: Inline.LowHalf[csD.GetPosition[pressStream]/512],
nRecords: intC.fontDirectorySegment.pages,
entityListPadding: 177777B];
fontDirectory ← lsD.SwapInStateSegment[intC.fontDirectorySegment];
csD.WriteBlock[pressStream, LOOPHOLE[fontDirectory], 0, SIZE[FontDirectoryRec] * 2];
lsD.ReleaseStateSegment[intC.fontDirectorySegment];
[] ← AdvancePressStreamToNextPage[];
partDirStartPosition ← csD.GetPosition[pressStream];
csD.WriteBlock
[partDirectoryStream, LOOPHOLE[@partDirectoryEntry], 0, SIZE[PartDirectoryEntry]];
csD.SetPosition[partDirectoryStream, 0];
csD.StreamCopy[partDirectoryStream, pressStream, csD.GetLength[partDirectoryStream]];
[] ← AdvancePressStreamToNextPage[];

documentDirectoryHeader ← DocumentDirectoryHeader
[generalPassword: 27183,
nRecordsInFile: Inline.LowHalf[csD.GetPosition[pressStream]/512] + 1,
nParts: Inline.LowHalf[csD.GetLength[partDirectoryStream] / SIZE[PartDirectoryEntry]],
partDirectoryStartRecord: Inline.LowHalf[partDirStartPosition/512],
nRecordsInPartDirectory:
Inline.LowHalf[(csD.GetPosition[pressStream] - partDirStartPosition)/512],
obsoleteBackPointer: 0,
unused1: DMSTimeDefs.currentTime.highbits,
unused2: DMSTimeDefs.currentTime.lowbits,
firstCopy: 1,
lastCopy: intC.hardCopies];
csD.Reset[partDirectoryStream];
csD.WriteBlock[pressStream, LOOPHOLE[@documentDirectoryHeader], 0,
SIZE[DocumentDirectoryHeader] * 2];
THROUGH [20 .. 256) DO WritePressByte[377B]; ENDLOOP;
IF chunk = 1 THEN pressFileName.length ← 4
ELSE {pressFileName.length ← 12; pressFileName[11] ← ’0 + chunk MOD 10};
WritePressString[pressFileName, 51];

userNameString.length ← 0;
FOR i IN [0 .. intC.hardcopyUserName.length) DO
IF intC.hardcopyUserName[i] = ’$ THEN
String.AppendString
[userNameString, intC.user.name ! String.StringBoundsFault => EXIT]
ELSE String.AppendChar
[userNameString, intC.hardcopyUserName[i] ! String.StringBoundsFault => EXIT];
ENDLOOP;
WritePressString[userNameString, 31];

pt.lc ← TimeDefs.CurrentDayTime[];
DMSTimeDefs.MapPackedTimeToTimeZoneString[pt, timeString];
WritePressString[timeString, 39];

THROUGH [380 .. 512) DO WritePressByte[0]; ENDLOOP;
END; -- of FinishPressFile --


PrintPressString: PUBLIC PROCEDURE [string: STRING, justify: BOOLEAN, x, y: Mica,
segTable: POINTER TO LineSegmentTable] =
-- Puts string into the press file entity list and data list.
BEGIN
start, end: CARDINAL ← 0;
spaceWidth: Mica ← widthTable[currentFont][Ascii.SP];

PrintSubString: PROCEDURE [x, y: Mica, start, length: CARDINAL] =
BEGIN
IF length = 0 THEN RETURN;
IF spaceWidth # currentSpaceWidth THEN
WriteCommandAndMica[setSpaceXCom, (currentSpaceWidth ← spaceWidth)];
WriteCommandAndMica[setXCom, x];
WriteCommandAndMica[setYCom, y];
-- output string data
csD.WriteBlock[pressStream, LOOPHOLE[@string.text], start, length];
-- output string description
IF length <= 32 THEN
WriteEntityByte[showCharactersShortCom + length - 1]
ELSE {WriteEntityByte[showCharactersCom]; WriteEntityByte[length]};
END; -- of PrintSubString --

IF segTable = NIL THEN PrintSubString[x, y, 0, string.length]
ELSE
BEGIN
IF justify AND segTable[1].index = string.length THEN
BEGIN -- compute space width for justified output.
width: Mica ← segTable[1].x - x;
blackWidth: Mica ← 0;
nBlanks: CARDINAL ← 0;
FOR i: CARDINAL IN [0 .. string.length) DO
char: CHARACTER ← string[i];
charWidth: Mica ← widthTable[currentFont][char];
SELECT TRUE FROM
(char = Ascii.SP) => nBlanks ← nBlanks + 1;
(charWidth # magicNonPrintingWidth) => blackWidth ← blackWidth + charWidth;
ENDCASE;
ENDLOOP;
IF nBlanks > 0 THEN spaceWidth ← (width - blackWidth) / nBlanks;
PrintSubString[x, y, 0, string.length];
END
ELSE
FOR i: CARDINAL IN [0 .. 12) UNTIL end = string.length DO
start ← end;
end ← segTable[i + 1].index;
PrintSubString[segTable[i].x, y, start, end - start];
ENDLOOP;
END;
END; -- of PrintPressString --


SetCurrentPressFont: PUBLIC PROCEDURE [font: FontNumber] =
-- Sets font in the press file entity list.
BEGIN
currentFont ← font;
WriteEntityByte[fontCom + font];
WriteCommandAndMica[setSpaceXCom, (currentSpaceWidth ← widthTable[font][Ascii.SP])];
END; -- of SetCurrentPressFont --


currentFont: FontNumber;
currentSpaceWidth: Mica;



-- ************************
-- Press File Transmission
-- ************************


FindPrinter: PUBLIC PROCEDURE RETURNS [found: BOOLEAN] =
-- Locates printer to which press file will be sent. Returns ok if found, cantConnect if not.
BEGIN
found ← TRUE;
who ← [ , , PupTypes.eftpReceiveSoc];
PupDefs.GetPupAddress[@who, intC.hardcopyHost !
PupDefs.PupNameTrouble =>
BEGIN
exD.DisplayBothExceptionLines[NIL, exD.cantFindPrinter, e, exD.nil];
found ← FALSE;
CONTINUE
END];
END; -- of FindPrinter --


TimeToSend: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
-- Returns TRUE iff press file exceeds maximum length.
BEGIN
RETURN[csD.GetPosition[pressStream]/512 > nPressFilePages];
END; -- of TimeToSend --


SendPressFile: PUBLIC PROCEDURE =
-- Uses EFTP to send the current press file to the current hardcopy host.
BEGIN
OPEN EFTPDefs, String;
bytesInPressStream: csD.Position = csD.GetPosition[pressStream];
utilityString: STRING ← [80];

BEGIN -- block for error exits
EFTPOpenForSending[who !
EFTPTimeOut =>
BEGIN
exD.DisplayBothExceptionLines
[NIL, exD.printerNotRespond, NIL, exD.cancelHardcopy, FALSE];
CheckForAbort[ ! AbortHardcopy => GO TO AbortSend];
-- sees DEL (or CANCEL) on second timeout
RESUME
END;
EFTPTroubleSending =>
IF e = eftpReceiverBusyAbort THEN
BEGIN
exD.DisplayBothExceptionLines
[s, exD.printerBusy, NIL, exD.cancelHardcopy, FALSE];
CheckForAbort[ ! AbortHardcopy => GO TO AbortSend];
-- sees DEL (or CANCEL) on second signal
RETRY
END
ELSE BEGIN
exD.GetExceptionString[exD.cannotConnectToPrinter, utilityString];
AppendString[utilityString, intC.hardcopyHost];
IF s ~= NIL AND s.length > 0 THEN
{AppendString[utilityString, ": "L]; AppendString[utilityString, s]};
exD.DisplayBothExceptionLines[utilityString, exD.nil, NIL, exD.hardcopyCanceled];
GO TO KillSend
END];
exD.GetExceptionString[exD.transmissionTo, utilityString];
AppendString[utilityString, intC.hardcopyHost];
exD.AppendExceptionString[exD.proceeding, utilityString];
exD.DisplayExceptionStringOnLine[utilityString, 1];

BEGIN
ENABLE
BEGIN
EFTPTimeOut =>
BEGIN
exD.DisplayBothExceptionLines[NIL, exD.printerTimeout, NIL, exD.hardcopyCanceled];
GO TO KillSend
END;
EFTPTroubleSending =>
BEGIN
exD.GetExceptionString[exD.printerTrouble, utilityString];
IF s # NIL THEN AppendString[utilityString, s ! StringBoundsFault => CONTINUE];
exD.DisplayBothExceptionLines[utilityString, exD.nil, NIL, exD.hardcopyCanceled];
GO TO KillSend
END
END;
IF intC.twoSidedPrinting THEN SendSpruceString["((DUPLEX TRUE))"L];
IF intC.passwordPrinting THEN
BEGIN
string: STRING ← [60];
String.AppendString[string, "((HOLD "L];
String.AppendString[string, intC.user.password];
String.AppendString[string, "))"L];
SendSpruceString[string];
END;
csD.SetPosition[pressStream, 0];
csD.ReadStream[pressStream, bytesInPressStream, SendPressStreamBlock];
csD.SetPosition[pressStream, 0];
CheckForAbort[ ! AbortHardcopy => GO TO AbortSend];
EFTPFinishSending[];
END;

RETURN;

EXITS
KillSend => aborted ← laurel;
AbortSend => NULL;
END;

EFTPAbortSending[""L];
ERROR AbortHardcopy

END; -- of SendPressFile --


SendPressStreamBlock: PROCEDURE [s: Stream.Block] =
{EFTPDefs.EFTPSendBlock
[Inline.LowHalf[s.blockPointer+s.startIndex/2], s.stopIndexPlusOne-s.startIndex]};

SendSpruceString: PROCEDURE [s: STRING] =
BEGIN
hackArray: POINTER TO ARRAY [0 .. 1] OF CARDINAL ← LOOPHOLE[s];
length, maxLength: CARDINAL;
length ← hackArray[0];
maxLength ← hackArray[1];
hackArray[0] ← 125314B;
hackArray[1] ← 170377B;
EFTPDefs.EFTPSendBlock[hackArray, 2 * String.WordsForString[length]];
hackArray[0] ← length;
hackArray[1] ← maxLength;
END; -- of SendSpruceString --


OpenPressStreams: PUBLIC PROCEDURE =
-- Opens streams used during press file construction.
BEGIN
pressStream ← csD.OpenFromName["Swatee"L, byte, write];
partDirectoryStream ← csD.Open[NIL, word, write];
entityListStream ← csD.Open[NIL, byte, write];
END; -- of OpenPressStreams --


ClosePressStreams: PUBLIC PROCEDURE =
-- Closes streams used during press file construction.
BEGIN
csD.Destroy[entityListStream];
csD.Destroy[partDirectoryStream];
csD.Destroy[pressStream];
END; -- of ClosePressStreams --


END. -- of IntPress --