DIRECTORY
Buttons USING [ButtonProc, Create, Button, ReLabel],
Commander USING [CommandProc, Handle, Register],
ConvertUnsafe USING [AppendRope],
Environment USING [Block, bytesPerPage, charsPerWord, wordsPerPage],
FileStream USING [SetLength],
Inline USING [LongDivMod],
IO USING [STREAM, Flush, Put, PutChar, PutRope, GetChar, CharsAvail, rope, char, string, card, NUL, SP, UserAbort],
LongString USING [AppendString, EquivalentStrings],
LongStorage USING [CopyString, FreeString, FreeStringNil, FreePages, Pages],
MailFile USING [Create, Destroy, Error, GetAttributes, Handle, MsgBody, TOC, TOCEntry],
Press
USING [
Abort, BadParameters, Character, Finish, FlushFontBuffers, FontIndex,
FontSlope, FontWeight, GetCurrentPageNumber, GetCurrentPosition, GetHeightOfFont,
GetWidthOfCharacter, Initialize, InternalError, Mica, micasPerInch, Mode,
pageHeight, pageWidth, PartBufferOverflow, PieceOfLine, Points, PutFontInTable, Reset,
SetCurrentFont, SetCurrentPosition, SetCurrentPageNumber, SetCurrentTabWidth, SetHeaderText,
SetMargins, SetMode, SkipSomeSpace, SetTrailerText, Start],
PressUtilities
USING [
hardcopyFont, hardcopyHost, IsPressFile, SendPressStream, ServerBusy, ServerTimeout,
ServerTrouble, SetupFontsForNonProgDisk, SetupHardCopyOptions],
PrintOps
USING [
DataEnd, Direction, Format, GetStatus, Margins, Parameters, ParametersHandle,
PressProcsObject],
Process USING [Yield, Detach],
Rope USING [ROPE, Length, Fetch],
Segments
USING [
FileNameProblem, FHandle, GetFileTimes, LockFile, NewFile, ReleaseFile, UnlockFile],
Streams
USING [
CreateStream, Destroy, GetIndex, NewStream, Read, ReadWrite,
GetBlock, SetIndex, Handle,
Write],
String USING [AppendChar, AppendString, EqualString],
Time USING [Append, Current, Packed, Unpack],
UserCredentials USING [GetUserCredentials],
UserProfile,
ViewerIO USING [CreateMessageWindowStream],
ViewerTools USING [GetSelectionContents];
PrintControl:
MONITOR
IMPORTS
Buttons, Commander, ConvertUnsafe, FileStream, Inline, IO, LongStorage, LongString, MailFile, Press, PressUtilities, PrintOps, Process, Rope, Segments, Streams, String, Time, UserCredentials, UserProfile, ViewerIO, ViewerTools
EXPORTS PrintOps =
BEGIN OPEN IO;
NUL: CHARACTER = IO.NUL;
Mica: TYPE = Press.Mica;
Inch: Mica = Press.micasPerInch;
charsPerWord: CARDINAL = Environment.charsPerWord;
bytesPerPage: CARDINAL = Environment.bytesPerPage;
debugging: BOOLEAN ← FALSE;
abortPlease: BOOLEAN ← FALSE;
busy: BOOLEAN ← FALSE;
printButton: Buttons.Button;
controllingExec: Commander.Handle; -- set up by CommandProc
out: PUBLIC IO.STREAM; -- controllingExec.out. set up by commandProc.
SetDebugging:
PROCEDURE [d:
BOOLEAN] =
BEGIN OPEN IO;
debugging ← d;
out.Put[rope["Debugging "], rope[IF debugging THEN "on\n" ELSE "off\n"]];
RETURN
END;
SetFont:
PROCEDURE [f:
STRING, p: PrintOps.ParametersHandle] =
BEGIN
IF font # NIL AND LongString.EquivalentStrings[f, font] THEN RETURN;
LongStorage.FreeString[font];
font ← LongStorage.CopyString[f];
fontChanged ← TRUE;
RETURN
END;
SetHost:
PROCEDURE [h:
STRING, p: PrintOps.ParametersHandle] =
BEGIN
LongStorage.FreeString[outputFile];
outputFile ← NIL;
transmitting ← TRUE;
IF printerName #
NIL
AND ~LongString.EquivalentStrings[h, printerName]
THEN
FinishFile[];
LongStorage.FreeString[printerName];
printerName ← LongStorage.CopyString[h];
haveStatus ← FALSE;
RETURN
END;
SetOutputFile:
PROCEDURE [f:
STRING, p: PrintOps.ParametersHandle] =
BEGIN OPEN String;
FinishFile[];
LongStorage.FreeString[printerName];
printerName ← NIL;
LongStorage.FreeString[outputFile];
FOR i:
CARDINAL
IN [0..f.length)
DO
IF f[i] = '. THEN {IF i = f.length - 1 THEN AppendString[f, "press"L]; EXIT}
REPEAT FINISHED => {AppendChar[f, '.]; AppendString[f, "press"L]};
ENDLOOP;
outputFile ← LongStorage.CopyString[f];
transmitting ← FALSE;
out.Put[rope["Output to "], string[f], char['\n]];
out.Flush[];
RETURN
END;
SetLandscape:
PROCEDURE [c:
CARDINAL, p: PrintOps.ParametersHandle] =
BEGIN
p.mode ← landscape;
IF ~fontSpecified THEN SetFont[lDefault.font, p];
p.columns ← c;
p.margins ← lDefault.margins;
RETURN
END;
SetPortrait:
PROCEDURE [c:
CARDINAL, p: PrintOps.ParametersHandle] =
BEGIN
p.mode ← portrait;
IF ~fontSpecified THEN SetFont[pDefault.font, p];
p.columns ← c;
p.margins ← pDefault.margins;
END;
SetNonProg:
PROCEDURE [p: PrintOps.ParametersHandle] =
BEGIN
p.nonprog ← TRUE;
p.trailers ← FALSE;
PressUtilities.SetupFontsForNonProgDisk[p.mode = landscape];
fontChanged ← FALSE;
IF p.mode = portrait THEN p.margins ← nonProgMargins;
END;
SetMail: PROCEDURE [p: PrintOps.ParametersHandle] = INLINE {p.mail ← TRUE};
SetTabWidth:
PROCEDURE [c:
CARDINAL, p: PrintOps.ParametersHandle] =
BEGIN p.tab ← c; END;
SetCopies:
PROCEDURE [c:
CARDINAL, p: PrintOps.ParametersHandle] =
BEGIN p.copies ← c; END;
sides: CARDINAL;
PropList: PROCEDURE [POINTER] RETURNS [CARDINAL] ← NIL;
SetSides:
PROCEDURE [s:
CARDINAL, p: PrintOps.ParametersHandle] =
BEGIN sides ← s; PropList ← IF sides = 0 THEN NIL ELSE MakePropertyList; END;
AppendBooleanProperty:
PROCEDURE [s, prop:
STRING, b:
BOOLEAN] =
BEGIN OPEN String;
AppendChar[s, '(];
AppendString[s, prop];
AppendChar[s, IO.SP];
AppendString[s, IF b THEN "TRUE"L ELSE "FALSE"L];
AppendChar[s, ')];
RETURN
END;
MakePropertyList:
PROCEDURE [p:
POINTER]
RETURNS [bytes:
CARDINAL] =
BEGIN OPEN String;
passwordPtr: TYPE = POINTER TO MACHINE DEPENDENT RECORD [a, b: CARDINAL];
s: STRING ← p;
s^ ← [length: 0, maxlength: 508, text:];
AppendChar[s, '(];
AppendBooleanProperty[s, "DUPLEX"L, sides = 2];
AppendChar[s, ')];
bytes ← s.length + 4;
LOOPHOLE[p, passwordPtr]^ ← [125314B, 170377B];
RETURN
END;
pressFileActive: BOOLEAN;
pressFileName: LONG STRING ← NIL;
AbortFile:
PROCEDURE [s: Rope.
ROPE] =
BEGIN
out.PutRope[s];
out.Flush[];
Press.Abort[];
pressFileActive ← FALSE;
END;
PressThisFile:
PROCEDURE [file:
STRING, selection: Rope.
ROPE] =
BEGIN
ENABLE
BEGIN
Segments.FileNameProblem[] =>
{out.Put[rope[" Can't find "], string[name]]; out.Flush[]; CONTINUE};
Press.BadParameters =>
{AbortFile["Bad parameters"]; IF ~debugging THEN CONTINUE};
Press.InternalError =>
{AbortFile["Internal Error"]; IF ~debugging THEN CONTINUE};
Press.PartBufferOverflow =>
{AbortFile["Document too complicated"]; IF ~debugging THEN CONTINUE};
END;
isPressFile: BOOLEAN;
lastPage: CARDINAL;
header: STRING ← [60];
fh: Segments.FHandle;
createTime: Time.Packed;
IF transmitting
AND ~haveStatus
AND ~PrintOps.GetStatus[]
THEN
BEGIN
IF controllingExec #
NIL
THEN {
execIn: IO.STREAM = controllingExec.in;
WHILE execIn.CharsAvail[]
DO [] ← execIn.GetChar[] ENDLOOP;
};
ERROR Abort;
END;
IF String.EqualString[file, "$$$"L] THEN {PressCurrentSelection[selection]; RETURN};
fh ← Segments.NewFile[file];
createTime ← Segments.GetFileTimes[fh].create;
haveStatus ← TRUE;
String.AppendString[header, file];
String.AppendString[header, " "L];
Time.Append[header, Time.Unpack[createTime]];
IF pressFileActive
AND outputFile =
NIL
AND (Streams.GetIndex[
outputStream] / bytesPerPage) > 200 THEN FinishFile[];
out.Put[rope["Pressing "], string[file]];
ShowParameters[];
out.PutRope["..."];
out.Flush[];
[isPressFile, lastPage] ← PressUtilities.IsPressFile[fh];
IF isPressFile
THEN
BEGIN
out.PutRope["already in Press format..."];
IF transmitting
THEN
BEGIN OPEN Streams, PressUtilities;
s: Streams.Handle = CreateStream[fh, Read];
aborted: BOOLEAN ← FALSE;
out.Put[rope["sending to "], string[printerName], rope["..."]];
out.Flush[];
IF AbortRequested[] THEN SIGNAL Abort;
SendPressStream[
s, lastPage, printerName, cParameters.copies, PropList, file, createTime !
ServerBusy => {Wait["busy", 8]; RESUME};
ServerTimeout => {Wait["not responding", 0]; RESUME};
ServerTrouble => {
IF message # NIL THEN out.Put[string[message]];
out.Flush[];
aborted ← TRUE;
CONTINUE};
UNWIND => {Press.Abort[]; Streams.Destroy[s]}];
out.PutRope[IF aborted THEN "Aborted\n" ELSE "Done\n"];
out.Flush[];
Streams.Destroy[s];
END
ELSE {out.PutRope["skipped"]; out.Flush[]; Segments.ReleaseFile[fh]};
RETURN
END;
Press.SetHeaderText[NIL, FALSE];
IF pressFileActive
THEN
THROUGH [0..neededFFs)
DO
Press.Character['\f];
Press.SetTrailerText[NIL, FALSE]; -- keep trailer on last page
ENDLOOP;
Press.SetCurrentPageNumber[1];
IF cParameters.headers THEN Press.SetHeaderText[header, TRUE];
IF cParameters.trailers
THEN Press.SetTrailerText[header,
TRUE];
BEGIN OPEN Press;
pages: CARDINAL;
IF fontChanged THEN InstallFont[font];
SetMode[cParameters.columns, cParameters.betweenColumns, cParameters.mode];
SetMargins[
cParameters.margins[left], cParameters.margins[right],
cParameters.margins[top], cParameters.margins[bottom]];
IF ~pressFileActive THEN StartFile[file];
SetCurrentFont[defaultFont, cParameters.weight, cParameters.slope];
SetCurrentTabWidth[GetWidthOfCharacter[' ]*cParameters.tab];
out.Put[card[pages ← PrintFile[fh]], rope[" page"]];
IF pages # 1 THEN out.PutChar['s];
END;
out.PutChar['\n];
out.Flush[];
RETURN
END;
transmitting: BOOLEAN;
bufferStream: Streams.Handle;
outputStream: Streams.Handle;
StartFile:
PROCEDURE [name:
STRING] =
BEGIN OPEN Streams;
s: STRING = [40];
IF outputFile =
NIL
THEN {
LongString.AppendString[s, bufferFile];
IF bufferStream =
NIL
THEN
bufferStream ← NewStream[s, ReadWrite];
outputStream ← bufferStream}
ELSE {
LongString.AppendString[s, outputFile];
outputStream ← NewStream[s, IF transmitting THEN ReadWrite ELSE Write]};
Press.Start[name, outputStream];
pressFileActive ← TRUE;
LongStorage.FreeString[pressFileName];
pressFileName ← LongStorage.CopyString[name];
END;
FinishFile:
PROCEDURE =
BEGIN
aborted: BOOLEAN ← FALSE;
lastPage: CARDINAL;
index: LONG CARDINAL;
IF ~pressFileActive THEN RETURN;
Press.Finish[];
index ← Streams.GetIndex[outputStream];
lastPage ← Inline.LongDivMod[index, bytesPerPage].quotient;
IF transmitting
THEN
BEGIN OPEN PressUtilities;
out.Put[rope["sending to "], string[printerName], rope["..."]];
FileStream.SetLength[outputStream, index];
Streams.SetIndex[outputStream, 0];
out.Flush[];
IF AbortRequested[] THEN SIGNAL Abort;
SendPressStream[
outputStream, lastPage, printerName, cParameters.copies, PropList,
pressFileName !
ServerBusy => {Wait["busy", 8]; RESUME};
ServerTimeout => {Wait["not responding", 0]; RESUME};
ServerTrouble => {
IF message # NIL THEN {out.Put[string[message]]; out.Flush[];};
aborted ← TRUE;
CONTINUE}];
END;
IF outputFile #
NIL
THEN {
FileStream.SetLength[outputStream, Streams.GetIndex[outputStream]];
Streams.Destroy[outputStream];
outputStream ← NIL};
out.PutRope[IF aborted THEN "...Aborted\n" ELSE "Done\n"];
out.Flush[];
pressFileActive ← FALSE;
END;
ComputeLineWidth:
PROCEDURE [p: PrintOps.ParametersHandle]
RETURNS [width: Mica] =
BEGIN
SELECT p.mode
FROM
portrait => width ← Press.pageWidth - p.margins[right] - p.margins[left];
landscape => width ← Press.pageHeight - p.margins[top] - p.margins[bottom];
ENDCASE => ERROR;
width ← width - (p.columns - 1)*(p.betweenColumns);
width ← LOOPHOLE[width, CARDINAL]/p.columns;
RETURN
END;
neededFFs: CARDINAL;
bufferPages: CARDINAL = 10;
bufferWords: CARDINAL = bufferPages * Environment.wordsPerPage;
bufferChars: CARDINAL = bufferWords * charsPerWord;
PrintFile:
PROCEDURE [fh: Segments.FHandle]
RETURNS [lastPage:
CARDINAL] =
BEGIN OPEN Streams;
s: Handle ← CreateStream[fh, Read];
iBufIndex: CARDINAL ← 0;
buffer:
RECORD [
numChars: CARDINAL,
p: LONG POINTER TO PACKED ARRAY OF CHARACTER];
procs: PrintOps.PressProcsObject ← [
Press.Character,
Press.GetWidthOfCharacter,
Press.PieceOfLine,
Press.SetCurrentFont,
Press.SkipSomeSpace,
Press.SetCurrentPosition,
Press.GetCurrentPosition,
Press.GetCurrentPageNumber,
Press.SetCurrentPageNumber,
Press.GetHeightOfFont];
Cleanup: PROCEDURE = {Streams.Destroy[s]; LongStorage.FreePages[buffer.p]};
NextChar:
PROCEDURE
RETURNS [c:
CHARACTER] =
BEGIN OPEN Streams;
IF iBufIndex = buffer.numChars
THEN {
IF AbortRequested[] THEN SIGNAL Abort;
buffer.numChars ← GetBlock[
s, buffer.p, bufferWords] * charsPerWord;
IF buffer.numChars = 0 THEN {iBufIndex ← 0--NOTE PDR--; ERROR PrintOps.DataEnd};
IF GetIndex[s]
MOD 2 # 0
THEN
buffer.numChars ← buffer.numChars - 1;
iBufIndex ← 0};
c ← buffer.p[iBufIndex];
iBufIndex ← iBufIndex + 1;
RETURN
END;
buffer.p ← LongStorage.Pages[bufferPages];
buffer.numChars ← 0;
lastPage ← PrintOps.Format[NextChar, @procs, @cParameters ! UNWIND => Cleanup[]];
Cleanup[];
BEGIN
s: CARDINAL = IF sides = 2 THEN 2 ELSE 1;
neededFFs ← (cParameters.columns*s) - ((lastPage - 1) MOD (cParameters.columns*s));
END;
RETURN
END;
PressMail:
PROCEDURE [file:
STRING] =
BEGIN
ENABLE
BEGIN
MailFile.Error => {
IF code = fileNotFound
THEN {
out.Put[rope[" Can't find "], string[file], char['\n]];
out.Flush[];
CONTINUE}
ELSE {AbortFile["Mail file problem"]; IF ~debugging THEN CONTINUE}};
Segments.FileNameProblem[] =>
{out.Put[rope[" Can't find "], string[name], char['\n]];
out.Flush[];
CONTINUE};
Press.BadParameters =>
{AbortFile["Bad parameters"]; IF ~debugging THEN CONTINUE};
Press.InternalError =>
{AbortFile["Internal Error"]; IF ~debugging THEN CONTINUE};
Press.PartBufferOverflow =>
{AbortFile["Document too complicated"]; IF ~debugging THEN CONTINUE};
END;
header: STRING ← [60];
uName: STRING ← [60];
fh: Segments.FHandle;
mH: MailFile.Handle;
createTime: Time.Packed;
IF transmitting
AND ~haveStatus
AND ~PrintOps.GetStatus[]
THEN
BEGIN
IF controllingExec #
NIL
THEN {
execIn: IO.STREAM = controllingExec.in;
WHILE execIn.CharsAvail[]
DO [] ← execIn.GetChar[] ENDLOOP;
};
Segments.ReleaseFile[fh];
ERROR Abort;
END;
CheckForExtension[name: file, ext: ".mail"L];
fh ← Segments.NewFile[file];
createTime ← Segments.GetFileTimes[fh].create;
haveStatus ← TRUE;
String.AppendString[header, file];
String.AppendString[header, " "L];
Time.Append[header, Time.Unpack[createTime]];
Segments.ReleaseFile[fh];
IF pressFileActive
AND outputFile =
NIL
AND (Streams.GetIndex[
outputStream] / bytesPerPage) > 200 THEN FinishFile[];
out.Put[rope["Pressing mail file "], string[file]];
ShowParameters[];
out.PutRope["..."];
out.Flush[];
uName.length ← 0;
ConvertUnsafe.AppendRope[uName, UserCredentials.GetUserCredentials[].name];
mH ← MailFile.Create[file, uName];
Press.SetHeaderText[NIL, FALSE];
IF pressFileActive
THEN
THROUGH [0..neededFFs)
DO
Press.Character['\f];
Press.SetTrailerText[NIL, FALSE]; -- keep trailer on last page
ENDLOOP;
Press.SetCurrentPageNumber[1];
IF cParameters.headers THEN Press.SetHeaderText[header, TRUE];
IF cParameters.trailers
THEN Press.SetTrailerText[header,
TRUE];
BEGIN OPEN Press;
pages: CARDINAL;
IF fontChanged THEN InstallFont[font, TRUE];
SetMode[cParameters.columns, cParameters.betweenColumns, cParameters.mode];
SetMargins[
cParameters.margins[left], cParameters.margins[right],
cParameters.margins[top], cParameters.margins[bottom]];
IF ~pressFileActive THEN StartFile[file];
SetCurrentFont[defaultFont, cParameters.weight, cParameters.slope];
SetCurrentTabWidth[GetWidthOfCharacter[' ]*cParameters.tab];
out.Put[card[pages ← PrintMailFile[mH]], rope[" page"]];
IF pages # 1 THEN out.PutChar['s];
END;
out.PutChar['\n];
out.Flush[];
RETURN
END;
PrintMailFile:
PROCEDURE [mH: MailFile.Handle]
RETURNS [lastPage:
CARDINAL] =
BEGIN OPEN p: cParameters;
nMessages: CARDINAL = MailFile.GetAttributes[mH, NIL].nMessages - 1;
curMessage: CARDINAL ← 0;
state: {toc, between, cr1, cr2, heading, cr3, message} ← toc;
block: Environment.Block;
first: BOOLEAN ← TRUE;
procs: PrintOps.PressProcsObject ← [
Press.Character,
Press.GetWidthOfCharacter,
Press.PieceOfLine,
Press.SetCurrentFont,
Press.SkipSomeSpace,
Press.SetCurrentPosition,
Press.GetCurrentPosition,
Press.GetCurrentPageNumber,
Press.SetCurrentPageNumber,
Press.GetHeightOfFont];
NextChar:
PROCEDURE
RETURNS [c:
CHARACTER] =
BEGIN
PAC: TYPE = LONG POINTER TO PACKED ARRAY OF CHARACTER;
DO
IF block.startIndex = block.stopIndexPlusOne
THEN {
IF AbortRequested[] THEN SIGNAL Abort;
SELECT state
FROM
toc => {state ← cr1; RETURN['\f]};
cr1 => {cParameters.wrap ← FALSE; state ← cr2; RETURN['\n]};
cr2 => {
IF first THEN {curMessage ← 0; first ← FALSE}
ELSE IF (curMessage ← curMessage + 1) = nMessages THEN ERROR PrintOps.DataEnd;
state ← heading; RETURN['\n]};
heading => {
state ← cr3;
Press.SetCurrentFont[defaultFont+1, cParameters.weight, cParameters.slope];
block ← MailFile.TOCEntry[mH, curMessage]};
cr3 => {state ← message; RETURN['\n]};
ENDCASE => {
state ← cr1;
cParameters.wrap ← TRUE;
Press.SetCurrentFont[defaultFont, cParameters.weight, cParameters.slope];
block ← MailFile.MsgBody[mH, curMessage]}}
ELSE EXIT;
ENDLOOP;
c ← LOOPHOLE[block.blockPointer, PAC][block.startIndex];
block.startIndex ← block.startIndex + 1;
RETURN
END;
cParameters.wrap ← FALSE;
block ← MailFile.TOC[mH];
lastPage ← PrintOps.Format[NextChar, @procs, @cParameters ! UNWIND => MailFile.Destroy[mH]];
MailFile.Destroy[mH];
BEGIN
s: CARDINAL = IF sides = 2 THEN 2 ELSE 1;
neededFFs ← (p.columns*s) - ((lastPage - 1) MOD (p.columns*s));
END;
RETURN
END;
PressCurrentSelection:
PROC [source: Rope.
ROPE] =
BEGIN
ENABLE
BEGIN
Press.BadParameters =>
{AbortFile["Bad parameters"]; IF ~debugging THEN CONTINUE};
Press.InternalError =>
{AbortFile["Internal Error"]; IF ~debugging THEN CONTINUE};
Press.PartBufferOverflow =>
{AbortFile["Document too complicated"]; IF ~debugging THEN CONTINUE};
END;
IF pressFileActive
AND outputFile =
NIL
AND (Streams.GetIndex[
outputStream] / bytesPerPage) > 200 THEN FinishFile[];
IF Rope.Length[source] = 0
THEN {
out.PutRope["No valid selection"]; RETURN};
out.PutRope["Pressing current selection"];
ShowParameters[];
out.PutRope["..."];
out.Flush[];
Press.SetHeaderText[NIL, FALSE];
IF pressFileActive
THEN
THROUGH [0..neededFFs)
DO
Press.Character['\f];
Press.SetTrailerText[NIL, FALSE]; -- keep trailer on last page
ENDLOOP;
Press.SetCurrentPageNumber[1];
BEGIN OPEN Press;
pages: CARDINAL;
IF fontChanged THEN InstallFont[font];
SetMode[cParameters.columns, cParameters.betweenColumns, cParameters.mode];
SetMargins[
cParameters.margins[left], cParameters.margins[right],
cParameters.margins[top], cParameters.margins[bottom]];
IF ~pressFileActive THEN StartFile["CurrentSelection"L];
SetCurrentFont[defaultFont, cParameters.weight, cParameters.slope];
SetCurrentTabWidth[GetWidthOfCharacter[' ]*cParameters.tab];
out.Put[card[pages ← PrintSelection[source]], rope[" page"]];
IF pages # 1 THEN out.PutChar['s];
END;
out.PutChar['\n];
out.Flush[];
RETURN
END;
PrintSelection:
PROCEDURE [source: Rope.
ROPE]
RETURNS [lastPage:
CARDINAL] =
BEGIN
l: INT = Rope.Length[source];
index: CARDINAL ← 0;
procs: PrintOps.PressProcsObject ← [
Press.Character,
Press.GetWidthOfCharacter,
Press.PieceOfLine,
Press.SetCurrentFont,
Press.SkipSomeSpace,
Press.SetCurrentPosition,
Press.GetCurrentPosition,
Press.GetCurrentPageNumber,
Press.SetCurrentPageNumber,
Press.GetHeightOfFont];
NextChar:
PROCEDURE
RETURNS [c:
CHARACTER] =
BEGIN
IF AbortRequested[] THEN SIGNAL Abort;
IF index = l THEN ERROR PrintOps.DataEnd;
c ← Rope.Fetch[source, index];
index ← index + 1;
RETURN
END;
lastPage ← PrintOps.Format[NextChar, @procs, @cParameters];
BEGIN OPEN p: cParameters;
s: CARDINAL = IF sides = 2 THEN 2 ELSE 1;
neededFFs ← (p.columns*s) - ((lastPage - 1) MOD (p.columns*s));
END;
RETURN
END;
CheckForExtension:
PROCEDURE [name, ext:
LONG
STRING] =
BEGIN
FOR i:
CARDINAL
DECREASING
IN [0..name.length)
DO
IF name[i] = '. THEN RETURN;
ENDLOOP;
LongString.AppendString[name, ext];
END;
defaultFont: Press.FontIndex = 0;
InstallFont:
PROCEDURE [f:
LONG
STRING, mail:
BOOLEAN ←
FALSE] =
BEGIN
name: STRING ← [40];
b: BufferItem ← [0, f];
c: CHARACTER;
size: Press.Points;
FinishFile[];
DO
c ← GetChar[@b];
IF c = NUL THEN EXIT;
IF c IN ['0..'9] THEN BEGIN Backup[@b]; EXIT END;
String.AppendChar[name, c];
ENDLOOP;
size ← GetNumber[@b, 8];
cParameters.weight ← medium;
cParameters.slope ← regular;
UNTIL (c ← GetChar[@b]) =
NUL
DO
IF c = 'b THEN cParameters.weight ← bold
ELSE IF c = 'i THEN cParameters.slope ← italic;
ENDLOOP;
Press.FlushFontBuffers[];
Press.PutFontInTable[defaultFont, name, size];
Press.PutFontInTable[defaultFont+1, name, size+2];
fontChanged ← FALSE;
RETURN
END;
Abort: SIGNAL = CODE;
Wait:
PROCEDURE [why: Rope.
ROPE, howlong:
CARDINAL] =
BEGIN
start: Time.Packed;
out.Put[rope["\nServer "], rope[why], rope["...will retry...type CONTROL-DEL to abort"]];
out.Flush[];
start ← Time.Current[];
UNTIL Time.Current[] - start > howlong
DO
IF AbortRequested[] THEN SIGNAL Abort;
Process.Yield[];
ENDLOOP;
out.PutRope["..."];
out.Flush[];
END;
fontChanged, fontSpecified: BOOLEAN;
Defaults:
TYPE =
RECORD [
font: STRING, columns: CARDINAL, margins: PrintOps.Margins];
lDefault: Defaults = ["Gacha6", 2, [Inch*3/4, Inch/2, Inch/2, Inch/2]];
pDefault: Defaults = ["Gacha8", 1, [Inch*3/4, Inch*3/4, Inch*3/4, Inch*3/4]];
nonProgMargins: PrintOps.Margins = [Inch*7/6, Inch*7/6, Inch, Inch];
font, bufferFile, outputFile: LONG STRING ← NIL;
printerName: PUBLIC LONG STRING ← NIL;
haveStatus: BOOLEAN;
cParameters, dParameters: PrintOps.Parameters;
ShowParameters:
PROCEDURE =
BEGIN
out.Put[char['/], char[IF cParameters.mode = landscape THEN 'l ELSE 'p], card[cParameters.columns]];
IF ~cParameters.headers THEN out.PutRope["~a"];
IF ~cParameters.trailers THEN out.PutRope["~z"];
IF sides = 1
OR sides = 2
THEN out.Put[char['s], card[sides]];
END;
InitGlobalParameters:
PROCEDURE =
BEGIN OPEN PressUtilities;
dMode: Press.Mode ←
IF UserProfile.Boolean["Hardcopy.Landscape", TRUE] THEN landscape ELSE portrait;
dColumns:
CARDINAL ← UserProfile.Number[
"Hardcopy.Columns", IF dMode = landscape THEN lDefault.columns ELSE pDefault.columns];
dTab: CARDINAL ← UserProfile.Number["Hardcopy.Tab", 8];
fontSpecified ← FALSE;
dParameters ←
[copies: 1, tab: dTab,
margins: IF dMode = landscape THEN lDefault.margins ELSE pDefault.margins,
columns: dColumns, weight: medium, slope: regular, mode: dMode];
Press.Initialize[];
SetupHardCopyOptions[];
printerName ← LongStorage.CopyString[hardcopyHost];
font ← LongStorage.CopyString[
IF hardcopyFont # NIL AND hardcopyFont.length # 0 THEN hardcopyFont ELSE IF dMode = landscape THEN lDefault.font ELSE pDefault.font];
fontChanged ← TRUE;
fontSpecified ← FALSE;
bufferFile ← LongStorage.CopyString["Swatee"L];
outputFile ← NIL;
END;
SetGlobalParameters:
PROCEDURE [b: Buffer] =
BEGIN
d: PrintOps.ParametersHandle = @dParameters;
sense: BOOLEAN ← TRUE;
sc: CHARACTER;
UNTIL (sc ← GetChar[b]) =
NUL
DO
SELECT sc
FROM
'c => SetCopies[GetNumber[b, 1], d];
't => SetTabWidth[GetNumber[b, 8], d];
'l => SetLandscape[GetNumber[b, 2], d];
'p => SetPortrait[GetNumber[b, 1], d];
's => SetSides[GetNumber[b, 0], d];
'm => SetMail[d];
'n => SetNonProg[d];
'd => SetDebugging[~debugging];
'a => d.headers ← sense;
'z => d.trailers ← sense;
'-, '~ => {sense ← FALSE; LOOP};
ENDCASE;
sense ← TRUE;
ENDLOOP;
cParameters ← dParameters;
END;
InitCurrentParameters:
PROCEDURE =
BEGIN fontSpecified ← FALSE; cParameters ← dParameters; END;
BufferItem: TYPE = RECORD [p: CARDINAL, s: LONG STRING];
Buffer: TYPE = POINTER TO BufferItem;
GetChar:
PROCEDURE [b: Buffer]
RETURNS [c:
CHARACTER] =
BEGIN OPEN b;
c ← NUL;
IF p < s.length
THEN
BEGIN
c ← s[p];
p ← p + 1;
IF c IN ['A..'Z] THEN c ← LOOPHOLE[LOOPHOLE[c, CARDINAL] + 40B];
END;
RETURN
END;
Backup: PROCEDURE [b: Buffer] = BEGIN IF b.p # 0 THEN b.p ← b.p - 1; END;
GetNumber:
PROCEDURE [b: Buffer, default:
CARDINAL]
RETURNS [v:
CARDINAL] =
BEGIN
c: CHARACTER;
usedefault: BOOLEAN ← TRUE;
v ← 0;
WHILE (c ← GetChar[b])
IN ['0..'9]
DO
usedefault ← FALSE; v ← v*10 + (c - 60C); ENDLOOP;
IF c # NUL THEN Backup[b];
IF usedefault THEN RETURN[default];
END;
ProcessItem:
PROCEDURE [arg, switches:
STRING, selection: Rope.
ROPE] =
BEGIN
b: BufferItem ← [0, switches];
c: PrintOps.ParametersHandle = @cParameters;
sc: CHARACTER;
sense: BOOLEAN ← TRUE;
mail: BOOLEAN ← FALSE;
IF AbortRequested[] THEN SIGNAL Abort;
UNTIL arg.length = 0
OR (sc ← GetChar[@b]) =
NUL
DO
SELECT sc
FROM
'f => {SetFont[arg, c]; fontSpecified ← TRUE; arg.length ← 0};
'h => {SetHost[arg, c]; arg.length ← 0};
'o => {SetOutputFile[arg, c]; arg.length ← 0};
'c => {SetCopies[GetNumber[@b, 1], c]; SetCopies[c.copies, @dParameters]};
't => SetTabWidth[GetNumber[@b, 8], c];
'l => SetLandscape[GetNumber[@b, 2], c];
'p => SetPortrait[GetNumber[@b, 1], c];
's => SetSides[GetNumber[@b, 0], c];
'n => SetNonProg[c];
'm => SetMail[c];
'd => SetDebugging[~debugging];
'a => c.headers ← sense;
'z => c.trailers ← sense;
'-, '~ => {sense ← FALSE; LOOP};
ENDCASE => {out.Put[rope["Unknown switch = "], char[sc], char['\n]]; out.Flush[]};
sense ← TRUE;
ENDLOOP;
IF arg.length = 0 THEN SetGlobalParameters[@b]
ELSE BEGIN IF c.mail THEN PressMail[arg] ELSE PressThisFile[arg, selection]; InitCurrentParameters[]; END;
RETURN
END;
GetToken:
PROCEDURE [commandLine: Rope.
ROPE, x:
INT, token, switches:
STRING]
RETURNS [nextX: INT, found: BOOLEAN ← FALSE] =
BEGIN
s: STRING;
c: CHARACTER;
l: INT ← Rope.Length[commandLine];
token.length ← switches.length ← 0;
s ← token;
FOR nextX ← x, nextX + 1
WHILE nextX < l
DO
SELECT (c ← Rope.Fetch[commandLine, nextX])
FROM
IO.SP => IF found THEN EXIT;
'/ => s ← switches;
'\n, '; => EXIT;
ENDCASE => {found ← TRUE; String.AppendChar[s, c]};
ENDLOOP;
RETURN
END;