-- 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 --