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