PrintControl.mesa; edited by Johnsson on 17-Apr-81 9:14:29
edited by Paul Rovner on 8-Feb-82 10:20:52
edited by Warren Teitelman on March 8, 1983 2:41 pm
edited by Michael Plass on May 2, 1983 4:39 pm
edited by MBrown on July 2, 1982 4:10 pm
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: BOOLEANFALSE;
abortPlease: BOOLEANFALSE;
busy: BOOLEANFALSE;
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 STRINGNIL;
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: BOOLEANFALSE;
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: BOOLEANFALSE;
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: BOOLEANTRUE;
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: BOOLEANFALSE] =
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 STRINGNIL;
printerName: PUBLIC LONG STRINGNIL;
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: BOOLEANTRUE;
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: BOOLEANTRUE;
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: BOOLEANTRUE;
mail: BOOLEANFALSE;
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: BOOLEANFALSE] =
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;
Command: ENTRY PROCEDURE [param: Rope.ROPE, selection: Rope.ROPENIL, cmd: Commander.Handle ← NIL] =
BEGIN OPEN Streams, Segments;
ENABLE UNWIND => {controllingExec ← NIL; out ← NIL};
x: INT ← 0;
found: BOOLEAN;
arg: STRING ← [40];
switches: STRING ← [20];
b: BufferItem ← [0, switches];
swatee: FHandle ← NewFile["Swatee"L, ReadWrite];
controllingExec ← cmd;
out ← IF cmd = NIL THEN ViewerIO.CreateMessageWindowStream[] ELSE cmd.out;
busy ← TRUE;
Buttons.ReLabel[printButton, "AbortPrint"];
abortPlease ← FALSE;
LockFile[swatee];
haveStatus ← FALSE;
sides ← 0;
transmitting ← TRUE;
pressFileActive ← FALSE;
pressFileName ← NIL;
bufferStream ← NIL;
outputStream ← NIL;
neededFFs ← 0;
InitGlobalParameters[];
SetGlobalParameters[@b];
InitCurrentParameters[];
DO
[x, found] ← GetToken[param, x, arg, switches];
IF found
THEN ProcessItem[arg, switches, selection ! Abort => {out.PutRope["..ABORTED\n"]; out.Flush[]; EXIT}]
ELSE EXIT;
ENDLOOP;
FinishFile[! Abort => {out.PutRope["..ABORTED\n"]; out.Flush[]; CONTINUE}];
Press.Reset[];
IF bufferStream # NIL THEN {Streams.Destroy[bufferStream]; bufferStream ← NIL};
font ← LongStorage.FreeStringNil[font];
bufferFile ← LongStorage.FreeStringNil[bufferFile];
outputFile ← LongStorage.FreeStringNil[outputFile];
pressFileName ← LongStorage.FreeStringNil[pressFileName];
UnlockFile[swatee];
ReleaseFile[swatee];
Buttons.ReLabel[printButton, "PrintSelection"];
abortPlease ← FALSE;
busy ← FALSE;
controllingExec ← NIL; out ← NIL;
RETURN
END;
AbortRequested: PROC RETURNS [BOOLEAN] = {
IF abortPlease THEN RETURN [TRUE];
IF controllingExec # NIL THEN IF controllingExec.in.UserAbort THEN RETURN [TRUE];
RETURN [FALSE];
};
CVPrintProc: Buttons.ButtonProc = TRUSTED
{IF busy THEN abortPlease ← TRUE ELSE Process.Detach[FORK Command["$$$", ViewerTools.GetSelectionContents[]]]; -- selection computed here because once print starts outputting characters to typescript, selection changes.
};
CommandProc: Commander.CommandProc =
TRUSTED
BEGIN
Command[cmd.commandLine, NIL, cmd];
END;
Init: PROCEDURE =
{Commander.Register["Print", CommandProc, "..for printing stuff"];
printButton ← Buttons.Create[info: [name: "PrintSelection"], proc: CVPrintProc];
};
Main body
Init[];
END...
23-Mar-82 11:47:04 Fixed PrintSelection to compute current selection before doing anything else.
17-Apr-82 20:50:16 removed reference to anExecHandle.
June 18, 1982 12:04 pm converted to 3.2, IOSTREAM -> IO, change in call to Buttons.Create, make CommandProc be safe.
July 2, 1982 4:10 pm by MBrown: Added Hardcopy.Tab.
March 8, 1983 2:40 pm by Teitelman: controllingExec is now set each time a print operation is executed. This is to cover the case where printselection is used and the original exec that print was run in is now destroyed. PrintSelection now uses the default exec, and out is a dribblestream to that exec as well as to the messagewindow.
May 2, 1983 4:10 pm by Plass: Converted to use Commander instead of UserExec.