-- PrintControl.mesa; edited by Johnsson on November 3, 1980 8:49 AM
-- Converted to Laurel by Pier, 24-Jul-81 14:56:53
-- Edited by Brotz, 27-Jul-81 13:17:27
-- Edited by Pier, August 14, 1981 2:07 PM
-- Converted to Laurel 6.1 by Pier, 17-May-83 10:24:07
-- Edited by Taft, May 17, 1983 3:34 PM
DIRECTORY
AltoDefs USING [CharsPerWord, PageSize],
Ascii USING [ESC, ControlZ, CR, DEL, FF, NUL, SP, TAB],
ImageDefs USING [BcdTime],
IODefs USING [WriteChar, WriteDecimal, WriteLine, WriteString, ReadChar,
ReadEditedString, Rubout, LineOverflow],
intCommon,
LaurelExecDefs,
opD: FROM "OperationsDefs",
ovD: FROM "OverviewDefs",
csD: FROM "CoreStreamDefs",
Core: FROM "Core",
Press USING [
Abort, Character, Finish, FlushFontBuffers, FontIndex,
FontSlope, FontWeight, GetCurrentPageNumber, GetCurrentPosition,
GetWidthOfCharacter, Initialize, Mica, micasPerInch, Mode,
pageHeight, pageWidth, PieceOfLine, Points, PutFontInTable, SetCurrentFont,
SetCurrentPageNumber, SetCurrentTabWidth, SetHeaderText, SetMargins, SetMode,
SkipSomeSpace, SetTrailerText, Start],
PressUtilities USING [
hardcopyHost, IsPressFile, SendPressStream, ServerBusy, ServerTimeout,
ServerTrouble, SetupFontsForBravo, SetupHardCopyOptions],
PrintDefs USING [BravoIt, GetStatus, FinishPrintStorage, PError,
PErrorCode],
ProcessDefs USING [Yield],
String USING [AppendChar, AppendString, EquivalentStrings, StringBoundsFault],
Storage USING [String, Pages, FreeString, FreePages],
StreamDefs USING [StreamHandle],
Time USING [Append, Unpack],
VMDefs;
PrintControl: PROGRAM
IMPORTS
ImageDefs, IODefs, Press, LaurelExecDefs, PressUtilities,
PrintDefs, ProcessDefs, intCommon, Core, VMDefs, csD, opD,
String, Storage, Time
EXPORTS PrintDefs =
BEGIN
PError: PUBLIC ERROR[code: PrintDefs.PErrorCode] = CODE;
NUL: CHARACTER = Ascii.NUL;
Mica: TYPE = Press.Mica;
Inch: Mica = Press.micasPerInch;
CharsPerWord: CARDINAL = AltoDefs.CharsPerWord;
--global command line storage instead of comCM stream
comLine: STRING = [512];
comPos: CARDINAL ← 0; --current position in comLine
CopyString: PROCEDURE [old: STRING] RETURNS [new: STRING] =
BEGIN
IF old = NIL THEN RETURN[NIL];
new ← Storage.String[old.length];
String.AppendString[new, old];
RETURN
END;
FreeString: PROCEDURE [old: STRING] =
BEGIN IF old # NIL THEN Storage.FreeString[old]; RETURN END;
debugging: BOOLEAN ← FALSE;
SetDebugging: PROCEDURE [d: BOOLEAN] =
BEGIN
debugging ← d;
IODefs.WriteString["Debugging "L];
IODefs.WriteLine[IF debugging THEN "on"L ELSE "off"L];
IF debugging THEN
BEGIN FreeString[bufferFile]; bufferFile ← CopyString["Buffer.press$"]; END;
RETURN
END;
SetFont: PROCEDURE [f: STRING, p: POINTER TO Parameters] =
BEGIN
IF font # NIL AND String.EquivalentStrings[f, font] THEN RETURN;
FreeString[font];
font ← CopyString[f];
fontChanged ← TRUE;
RETURN
END;
SetHost: PROCEDURE [h: STRING, p: POINTER TO Parameters] =
BEGIN
IF outputFile # NIL THEN FreeString[outputFile];
outputFile ← NIL;
transmitting ← TRUE;
IF printerName # NIL AND ~String.EquivalentStrings[h, printerName] THEN
FinishFile[];
IF printerName # NIL THEN FreeString[printerName];
printerName ← CopyString[h];
haveStatus ← FALSE;
RETURN
END;
SetOutputFile: PROCEDURE [f: STRING, p: POINTER TO Parameters] =
BEGIN OPEN String;
userName: STRING = [40];
FinishFile[];
IF printerName # NIL THEN FreeString[printerName];
printerName ← NIL;
IF outputFile # NIL THEN 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 ← CopyString[f];
String.AppendString[userName, intCommon.user.name !
String.StringBoundsFault => GO TO sbF];
transmitting ← FALSE;
IODefs.WriteString["Output to "L];
IODefs.WriteLine[outputFile];
RETURN
EXITS sbF => SBFault[];
END;
SetLandscape: PROCEDURE [c: CARDINAL, p: POINTER TO Parameters] =
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: POINTER TO Parameters] =
BEGIN
p.mode ← portrait;
IF ~fontSpecified THEN SetFont[pDefault.font, p];
p.columns ← c;
p.margins ← pDefault.margins;
END;
SetTabWidth: PROCEDURE [c: CARDINAL, p: POINTER TO Parameters] =
BEGIN p.tab ← c; END;
SetCopies: PROCEDURE [c: CARDINAL, p: POINTER TO Parameters] =
BEGIN p.copies ← c; END;
SetBravo: PROCEDURE [p: POINTER TO Parameters, xx: BOOLEAN] =
BEGIN p.bravo ← xx; END;
sides: CARDINAL ← 0;
PropList: PROCEDURE [POINTER] RETURNS [CARDINAL] ← NIL;
SetSides: PROCEDURE [s: CARDINAL, p: POINTER TO Parameters] =
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, Ascii.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 ← FALSE;
PressThisFile: PROCEDURE [file: STRING] =
BEGIN
fh: VMDefs.FileHandle ← NIL;
BEGIN
ENABLE Abort, UNWIND => fh ← CloseF[fh];
MyFileError: PROCEDURE [reason: opD.FileErrorReason, fileName, errorString: STRING] =
BEGIN OPEN IODefs, opD;
WriteString[" Can't access "L];
WriteString[fileName];
WriteString[": "L];
WriteString[SELECT reason FROM
notFound => "file not found"L,
cantConnect => "can't connect to server"L,
illegalName => "illegal name"L,
diskFull => "disk full"L,
ftpError => "FTP error"L,
ENDCASE => ""L];
IF errorString#NIL THEN {WriteString[" -- "L]; WriteString[errorString]};
WriteLine[""L];
END;
overWriteTrue: PROCEDURE RETURNS [BOOLEAN] =
BEGIN RETURN[TRUE]; END;
c: CHARACTER;
createTime: LONG CARDINAL ← 0;
isPressFile: BOOLEAN;
lastPage: CARDINAL ← 0;
header: STRING ← [120];
Core.Login[@intCommon.user];
IF file[0]='[ THEN BEGIN
[] ← opD.Copy[file, "PRINT.TEMP$"L, overWriteTrue ! opD.FileError => {MyFileError[reason: reason, fileName: file, errorString: errorString]; GOTO exit;};];
fh ← Core.Open[filename:"PRINT.TEMP$"L, mode: read];
printTempted ← TRUE;
END
ELSE
fh ← Core.Open[filename: file, mode: read ! VMDefs.CantOpen, VMDefs.Error => {MyFileError[reason: notFound, fileName: file, errorString: NIL]; GOTO exit;};];
--Core.FreeCacheEntry[file];--
createTime ← VMDefs.GetFileTimes[fh].create;
IF transmitting AND ~haveStatus AND ~PrintDefs.GetStatus[] THEN
BEGIN
IODefs.WriteString["Type DEL to exit"L];
UNTIL (c ← IODefs.ReadChar[]) = Ascii.DEL DO ENDLOOP;
SIGNAL Abort;
END;
haveStatus ← TRUE;
String.AppendString[header, file ! String.StringBoundsFault => GO TO sbF;];
String.AppendString[header, " "L];
Time.Append[header, Time.Unpack[createTime]];
IF pressFileActive AND
outputFile = NIL AND
csD.MapPositionToPageByte[csD.GetPosition[outputStream]].page > 200
THEN FinishFile[];
IF writeMessages THEN
{IODefs.WriteString[IF cParameters.bravo THEN "Bravoing "L ELSE "Pressing "L];
IODefs.WriteString[file];
ShowParameters[];
IODefs.WriteString["..."L];} ELSE IODefs.WriteChar['*];
[isPressFile, lastPage] ← PressUtilities.IsPressFile[fh];
IF isPressFile THEN
BEGIN
IF writeMessages THEN IODefs.WriteString["already in Press format..."L];
IF transmitting THEN
BEGIN OPEN IODefs, csD, PressUtilities;
s: csD.StreamHandle ← csD.Open[fh, byte, read];
aborted: BOOLEAN ← FALSE;
WriteString["sending to "L];
WriteString[printerName];
WriteString["..."L];
SendPressStream[
s, lastPage, printerName, cParameters.copies, PropList !
ServerBusy => {aborted ← Wait["busy"L, 8];
IF aborted THEN CONTINUE ELSE RESUME};
ServerTimeout => {aborted ← Wait["not responding"L, 0];
IF aborted THEN CONTINUE ELSE RESUME};
ServerTrouble => {
IF message # NIL THEN WriteString[message];
aborted ← TRUE;
CONTINUE};
UNWIND => {Press.Abort[]; s ← DestroyS[s]; fh ← CloseF[fh];}];
WriteLine[IF aborted THEN "Aborted"L ELSE "Done"L];
s ← DestroyS[s];
END
ELSE IF writeMessages THEN IODefs.WriteString["skipped"L];
fh ← CloseF[fh];
RETURN
END;
Press.SetHeaderText[NIL, FALSE];
IF pressFileActive THEN
THROUGH [0..neededFFs) DO
Press.Character[Ascii.FF];
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];
IF cParameters.bravo THEN
BEGIN
IF ~bravoFonts THEN PressUtilities.SetupFontsForBravo[];
bravoFonts ← TRUE;
IF ~pressFileActive THEN StartFile[file];
PrintDefs.BravoIt[fh];
neededFFs ← 1;
END
ELSE
BEGIN OPEN Press;
pages: CARDINAL;
IF fontChanged OR bravoFonts THEN InstallFont[font];
SetMode[cParameters.columns, 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];
pages←PrintFile[fh];
IF writeMessages THEN
{IODefs.WriteDecimal[pages];
IODefs.WriteString[" page"L];
IF pages # 1 THEN IODefs.WriteChar['s];};
END;
fh ← CloseF[fh];
IF writeMessages THEN IODefs.WriteChar[Ascii.CR];
RETURN
EXITS sbF => SBFault[];
END;--of ENABLE block
EXITS
exit=> NULL;
END;--of PressThisFile
transmitting: BOOLEAN ← TRUE;
outputStream: csD.StreamHandle ← NIL;
StartFile: PROCEDURE [name: STRING] =
BEGIN
Core.Login[@intCommon.user];
IF outputFile = NIL THEN
BEGIN
IF outputStream = NIL THEN
BEGIN
outputStream ← csD.OpenFromName[bufferFile, byte, write];
--Core.FreeCacheEntry[bufferFile];--
END;
END
ELSE
BEGIN
outputStream ← csD.OpenFromName[outputFile, byte, write];
--Core.FreeCacheEntry[outputFile];--
END;
Press.Start[name, outputStream];
pressFileActive ← TRUE;
END;
FinishFile: PROCEDURE =
BEGIN OPEN IODefs;
aborted: BOOLEAN ← FALSE;
lastPage: CARDINAL ← 177777B;
IF ~pressFileActive THEN RETURN;
Press.Finish[];
IF outputStream # NIL THEN
[lastPage, ] ← csD.MapPositionToPageByte[csD.GetPosition[outputStream]];
IF transmitting AND lastPage # 177777B THEN
BEGIN OPEN PressUtilities;
WriteString["sending to "L];
WriteString[printerName];
WriteString["..."L];
csD.SetPosition[outputStream, 0];-- reset press file stream to zero
SendPressStream[
outputStream, lastPage, printerName, cParameters.copies, PropList !
ServerBusy => {aborted ← Wait["busy"L, 8];
IF aborted THEN CONTINUE ELSE RESUME};
ServerTimeout => {aborted ← Wait["not responding"L, 0];
IF aborted THEN CONTINUE ELSE RESUME};
ServerTrouble => {
IF message # NIL THEN WriteString[message];
aborted ← TRUE;
CONTINUE}];
END ELSE IF transmitting THEN aborted ← TRUE;
outputStream ← DestroyS[outputStream];
WriteLine[IF aborted THEN "...Aborted"L ELSE "Done"L];
pressFileActive ← FALSE;
END;
ComputeLineWidth: PROCEDURE [p: POINTER TO Parameters] 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 => PrintDefs.PError[PageModeError];
width ← width - (p.columns - 1)*(betweenColumns);
width ← LOOPHOLE[width, CARDINAL]/p.columns;
RETURN
END;
neededFFs: CARDINAL ← 0;
bufferPages: CARDINAL = 10;
bufferWords: CARDINAL = bufferPages * AltoDefs.PageSize;
bufferChars: CARDINAL = bufferWords * AltoDefs.CharsPerWord;
PrintFile: PROCEDURE [fh: VMDefs.FileHandle] RETURNS [lastPage: CARDINAL] =
BEGIN OPEN p: cParameters, Press;
s: csD.StreamHandle ← csD.Open[fh, byte, read];
b: STRING ← [250];
bol: BOOLEAN ← TRUE;
lineWidth: Mica = ComputeLineWidth[@cParameters];
i, whiteLength: CARDINAL ← 0;
curX, bWidth: Mica ← 0;
indentWidth, charWidth, whiteWidth: Mica;
spaceWidth: Mica = GetWidthOfCharacter[Ascii.SP];
tabWidth: Mica = spaceWidth*cParameters.tab;
char: CHARACTER;
ok: BOOLEAN;
iBufIndex: CARDINAL ← 0;
buffer: RECORD [
numChars: CARDINAL,
p: POINTER TO PACKED ARRAY [0..bufferChars) OF CHARACTER];
NextChar: PROCEDURE RETURNS [c: CHARACTER, ok: BOOLEAN ← TRUE] =
BEGIN
IF iBufIndex = buffer.numChars THEN {
buffer.numChars ← csD.ReadBlock[
s, buffer.p, 0, bufferChars];
IF buffer.numChars = 0 THEN RETURN[c: , ok: FALSE];
iBufIndex ← 0};
c ← buffer.p[iBufIndex];
iBufIndex ← iBufIndex + 1;
RETURN
END;
PutPiece: PROCEDURE =
BEGIN
IF i # 0 THEN
BEGIN
Press.PieceOfLine[b, bWidth];
bWidth ← 0;
b.length ← i ← whiteLength ← 0;
END;
END;
Overflow: PROCEDURE =
BEGIN
IF whiteLength = 0 OR whiteLength = b.length THEN
BEGIN PutPiece[]; Press.Character[Ascii.CR] END
ELSE
BEGIN
b.length ← whiteLength;
Press.PieceOfLine[b, whiteWidth];
Press.Character[Ascii.CR];
bWidth ← bWidth - whiteWidth - spaceWidth;
b.length ← i - whiteLength - 1;
FOR j: CARDINAL IN [0..b.length) DO
b[j] ← b[whiteLength + j + 1]; ENDLOOP;
i ← b.length;
whiteLength ← 0;
END;
IF indentWidth # 0 THEN Press.SkipSomeSpace[indentWidth];
curX ← indentWidth + bWidth;
END;
Cleanup: PROCEDURE =
BEGIN
Storage.FreePages[buffer.p];
s ← DestroyS[s];
RETURN
END;
buffer.p ← Storage.Pages[bufferPages];
buffer.numChars ← 0;
DO
ENABLE UNWIND => Cleanup[];
[char,ok] ← NextChar[];
IF ~ok THEN EXIT;
SELECT char FROM
Ascii.SP =>
BEGIN
IF bol THEN BEGIN curX ← curX + spaceWidth; LOOP END;
whiteLength ← i;
whiteWidth ← bWidth;
IF curX + spaceWidth > lineWidth THEN BEGIN Overflow[]; LOOP END;
END;
Ascii.CR, Ascii.FF =>
BEGIN
PutPiece[];
Press.Character[char];
bol ← TRUE;
indentWidth ← curX ← 0;
LOOP
END;
Ascii.TAB =>
BEGIN
IF bol THEN
BEGIN curX ← (((curX + spaceWidth)/tabWidth) + 1)*tabWidth; LOOP END;
PutPiece[];
Press.Character[char];
curX ← GetCurrentPosition[].x;
LOOP
END;
Ascii.ControlZ =>
BEGIN
PutPiece[];
UNTIL char = Ascii.CR DO
[char, ok] ← NextChar[];
IF ~ok THEN EXIT;
ENDLOOP;
Press.Character[char];
bol ← TRUE;
indentWidth ← curX ← 0;
LOOP
END;
ENDCASE =>
IF bol THEN
BEGIN
IF (indentWidth ← curX) # 0 THEN Press.SkipSomeSpace[indentWidth];
bol ← FALSE;
END;
charWidth ← GetWidthOfCharacter[char];
IF curX + charWidth > lineWidth THEN Overflow[];
IF i = b.maxlength THEN PutPiece[];
b[i] ← char;
b.length ← i ← i + 1;
bWidth ← bWidth + charWidth;
curX ← curX + charWidth;
ENDLOOP;
PutPiece[];
Cleanup[];
lastPage ← GetCurrentPageNumber[];
BEGIN
s: CARDINAL = IF sides = 2 THEN 2 ELSE 1;
neededFFs ← (p.columns*s) - ((lastPage - 1) MOD (p.columns*s));
END;
RETURN
END;
defaultFont: Press.FontIndex = 0;
InstallFont: PROCEDURE [f: STRING] =
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 ! String.StringBoundsFault => GO TO sbF;];
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];
bravoFonts ← fontChanged ← FALSE;
RETURN
EXITS sbF => SBFault[];
END;
Abort: SIGNAL = CODE;
TicksPerSec: CARDINAL = 25;
clock: POINTER TO INTEGER = LOOPHOLE[430B];
keys: StreamDefs.StreamHandle;
Wait: PROCEDURE [why: STRING, howlong: INTEGER] RETURNS[abort: BOOLEAN ← FALSE] =
BEGIN OPEN IODefs;
start: INTEGER;
WriteString["
Server "];
WriteString[why];
WriteString["...will retry...type DEL to abort"];
start ← clock↑;
UNTIL clock↑ - start > howlong*TicksPerSec DO
IF ~keys.endof[keys] AND
keys.get[keys] = Ascii.DEL THEN RETURN[TRUE];
ProcessDefs.Yield[];
ENDLOOP;
WriteString["..."];
END;
fontChanged, fontSpecified: BOOLEAN;
bravoFonts: BOOLEAN ← FALSE;
Defaults: TYPE = RECORD [
font: STRING, columns: CARDINAL, margins: ARRAY Direction OF Press.Mica];
lDefault: Defaults = ["Gacha6", 2, [Inch*3/4, Inch/2, Inch/2, Inch/2]];
pDefault: Defaults = ["Gacha8", 1, [Inch*3/4, Inch/2, Inch*3/4, Inch/2]];
betweenColumns: Mica = Inch/2;
Direction: TYPE = {left, right, top, bottom};
Parameters: TYPE = RECORD [
copies, tab: CARDINAL,
margins: ARRAY Direction OF Press.Mica,
columns: CARDINAL,
bravo: BOOLEAN,
headers: BOOLEAN ← TRUE,
trailers: BOOLEAN ← TRUE,
weight: Press.FontWeight,
slope: Press.FontSlope,
mode: Press.Mode];
font, bufferFile, outputFile: STRING;
printerName: PUBLIC STRING;
haveStatus: BOOLEAN ← FALSE;
cParameters, dParameters: Parameters;
ShowParameters: PROCEDURE =
BEGIN OPEN IODefs;
WriteChar['/];
IF cParameters.bravo THEN {WriteChar['b]; RETURN};
WriteChar[IF cParameters.mode = landscape THEN 'l ELSE 'p];
WriteDecimal[cParameters.columns];
IF ~cParameters.headers THEN WriteString["~a"L];
IF ~cParameters.trailers THEN WriteString["~z"L];
IF sides = 1 OR sides = 2 THEN {WriteChar['s]; WriteDecimal[sides]};
END;
InitGlobalParameters: PROCEDURE =
BEGIN OPEN PressUtilities;
fontSpecified ← FALSE;
dParameters ←
[copies: 1, tab: 8, margins: lDefault.margins, columns: lDefault.columns,
bravo: FALSE, weight: medium, slope: regular, mode: landscape];
Press.Initialize[];
SetupHardCopyOptions[];
printerName ← CopyString[hardcopyHost];
font ← CopyString[lDefault.font];
fontChanged ← TRUE;
fontSpecified ← FALSE;
bufferFile ← CopyString["Swatee"L];
outputFile ← NIL;
keys ← intCommon.keystream;
END;
SetGlobalParameters: PROCEDURE [b: Buffer] =
BEGIN
d: POINTER TO Parameters = @dParameters;
sense: BOOLEAN ← TRUE;
sc: CHARACTER;
UNTIL (sc ← GetChar[b]) = NUL DO
SELECT sc FROM
'b => SetBravo[d, TRUE];
'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];
'd => SetDebugging[~debugging];
'm => writeMessages ← sense;
'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: STRING];
Buffer: TYPE = POINTER TO BufferItem;
GetChar: PROCEDURE [b: Buffer] RETURNS [c: CHARACTER] =
BEGIN OPEN b;
c ← Ascii.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;
writeMessages: BOOLEAN ← TRUE;
ProcessItem: PROCEDURE [arg, switches: STRING] =
BEGIN
b: BufferItem ← [0, switches];
c: POINTER TO Parameters = @cParameters;
sc: CHARACTER;
sense: BOOLEAN ← TRUE;
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];
't => SetTabWidth[GetNumber[@b, 8], c];
'l => SetLandscape[GetNumber[@b, 2], c];
'p => SetPortrait[GetNumber[@b, 1], c];
's => SetSides[GetNumber[@b, 0], c];
'b => SetBravo[c, TRUE];
'd => SetDebugging[~debugging];
'a => c.headers ← sense;
'z => c.trailers ← sense;
'm => writeMessages ← sense;
'-, '~ => {sense ← FALSE; LOOP};
ENDCASE =>
BEGIN OPEN IODefs;
WriteString["Unknown switch = "L];
WriteChar[sc];
WriteChar[Ascii.CR];
END;
sense ← TRUE;
ENDLOOP;
IF arg.length = 0 THEN SetGlobalParameters[@b]
ELSE BEGIN PressThisFile[arg]; InitCurrentParameters[]; END;
RETURN
END;
GetToken: PROCEDURE [token, switches: STRING]
RETURNS [found: BOOLEAN ← FALSE] =
--uses global string comLine with current position comPos
BEGIN
s: STRING;
c: CHARACTER;
token.length ← switches.length ← 0;
s ← token;
WHILE comPos < comLine.length DO
SELECT (c ← comLine[comPos]) FROM
Ascii.CR, Ascii.SP => IF found THEN {comPos ← comPos + 1; EXIT;};
'/ => s ← switches;
'; => EXIT;
ENDCASE => {found ← TRUE;
String.AppendChar[s, c ! String.StringBoundsFault => GO TO sbF;]};
comPos ← comPos + 1;
ENDLOOP;
RETURN
EXITS sbF => SBFault[];
END;
escTrue: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
{RETURN [c = Ascii.ESC]};
Command: PROCEDURE =
BEGIN
arg: STRING ← [100];--large enough for 99 character file name
switches: STRING ← [20];
b: BufferItem ← [0, switches];
InitGlobalParameters[];
SetGlobalParameters[@b];
InitCurrentParameters[];
IODefs.WriteLine["Type command line, terminate with ESC"L];
IODefs.WriteChar[Ascii.CR];
comLine.length ← 0;
[] ← IODefs.ReadEditedString[comLine, escTrue, FALSE !
IODefs.Rubout => GO TO rubout;
IODefs.LineOverflow => {IODefs.WriteChar[Ascii.CR];
IODefs.WriteLine["***Command line truncated"L];
CONTINUE;};];
IODefs.WriteChar[Ascii.CR];
WHILE GetToken[arg, switches] AND NoDEL[] DO
ProcessItem[arg, switches]; ENDLOOP;
FinishFile[];
outputStream ← DestroyS[outputStream];
EXITS rubout => SIGNAL Abort;
END;
NoDEL: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
IF ~keys.endof[keys] AND
keys.get[keys] = Ascii.DEL THEN SIGNAL Abort;
RETURN[TRUE];
END;
Init: PROCEDURE =
BEGIN
t: STRING ← [20];
Time.Append[t, Time.Unpack[ImageDefs.BcdTime[]]];
t.length ← t.length - 9;
IODefs.WriteString["Print of "L];
IODefs.WriteLine[t];
BEGIN OPEN LaurelExecDefs;
MakeMenuCommandCallable[user];
MakeMenuCommandCallable[newMail];
MakeMenuCommandCallable[mailFile];
MakeMenuCommandCallable[display];
MakeMenuCommandCallable[delete];
MakeMenuCommandCallable[undelete];
MakeMenuCommandCallable[moveTo];
MakeMenuCommandCallable[copy];
END;
END;
SayPError: PROCEDURE[code: PrintDefs.PErrorCode] =
BEGIN OPEN PrintDefs;
IODefs.WriteChar[Ascii.CR];
IODefs.WriteString["PRINT ERROR: "L];
SELECT code FROM
BadParameters => IODefs.WriteLine["BadParameters"L];
InternalError => IODefs.WriteLine["InternalError"L];
MoreThan16Fonts => IODefs.WriteLine["MoreThan16Fonts"L];
ELBufferOverflow => IODefs.WriteLine["ELBufferOverflow"L];
PartBufferOverflow => IODefs.WriteLine["PartBufferOverflow"L];
UserCmMixup => IODefs.WriteLine["UserCmMixup"L];
FontNotInFontsDotWidths => IODefs.WriteLine["FontNotInFontsDotWidths"L];
ErrorReadingFontWidths => IODefs.WriteLine["ErrorReadingFontWidths"L];
FileNotPressFormat => IODefs.WriteLine["FileNotPressFormat"L];
PressThisFileError => IODefs.WriteLine["ErrorPressingFile"L];
InputFileError => IODefs.WriteLine["InputFileError"L];
ENDCASE => IODefs.WriteLine["UnknownError"L];
END;
DestroyS: PUBLIC PROCEDURE[s: csD.StreamHandle] RETURNS [csD.StreamHandle] =
BEGIN
IF s # NIL THEN csD.Destroy[s];
RETURN[NIL];
END;
CloseF: PUBLIC PROCEDURE[f: VMDefs.FileHandle] RETURNS [VMDefs.FileHandle] =
BEGIN
IF f # NIL THEN [] ← Core.Close[f];
RETURN[NIL];
END;
SBFault: PROCEDURE =
BEGIN
IODefs.WriteChar[Ascii.CR];
IODefs.WriteLine["StringBoundsFault"L]; SIGNAL Abort;
END;
-- Main body
printTempted: BOOLEAN ← FALSE;
pTempHandle: VMDefs.FileHandle ← NIL;
--swEC: ovD.ErrorCode;--
--swPage: crD.PageNumber ← 0;--
--swByte: crD.PageByte ← 0;--
--[swEC, swPage, swByte] ← GetSwatee[];----use Swatee as backing file--
Init[];
Command[ ! Abort => {IODefs.WriteLine["Aborted"L]; CONTINUE;};
PrintDefs.PError => {SayPError[code]; CONTINUE;};];
outputStream ← DestroyS[outputStream];
PrintDefs.FinishPrintStorage[];
IF ~debugging THEN
{ --RestoreSwatee[swEC, swPage, swByte];--
IF printTempted THEN {
Core.Login[@intCommon.user];
pTempHandle ← Core.Open["PRINT.TEMP$"L, update];
Core.Delete[pTempHandle];
};
};
END...
GetSwatee: PROCEDURE RETURNS[swatEC: ovD.ErrorCode,
swatlastPage: crD.PageNumber ←0,
swatbyteFF: crD.PageByte ←0] =
BEGIN
swatfh: crD.UFileHandle;
[swatEC, swatfh] ← crD.OpenFile[intCommon.user, "Swatee"L, update];
IF swatfh#NIL THEN [ , swatlastPage, swatbyteFF] ← crD.UFileLength[swatfh];
[] ← crD.CloseFile[swatfh];--noop if handle is NIL
END;
RestoreSwatee: PROCEDURE [swatEC: ovD.ErrorCode, lastPage: crD.PageNumber,
byteFF: crD.PageByte] =
BEGIN
swatfh: crD.UFileHandle;
ec: ovD.ErrorCode;
[ec, swatfh] ← crD.OpenFile[intCommon.user, "Swatee"L, update];
IF ec = ovD.ok AND swatEC = ovD.ok THEN []←crD.UFileTruncate[lastPage, byteFF, swatfh];
[] ← crD.CloseFile[swatfh];--noop if handle is NIL
END;