DIRECTORY Commander USING [CommandProc, Handle, Register], CommandTool USING [ArgumentVector, CurrentWorkingDirectory, Failed, Parse], Convert USING [AppendInt, Error, IntFromRope], FS USING [ComponentPositions, Delete, EnumerateForNames, ExpandName, Error, ErrorDesc, ErrorFromStream, FileInfo, NameProc, StreamOpen], IO USING [Backup, CR, Close, FF, EndOfStream, Error, GetChar, GetLength, GetIndex, int, PutChar, PutF, PutFR, PutRope, rope, SetLength, SP, STREAM, TAB, time], PressPrinter USING [Abort, CurrentState, CurrentStateMessage, Handle, IsAPressFile, SendPressFile, State], Process USING [CheckForAbort], PutGet USING [FromFile, FromFileError], Real USING [RoundLI], RefText USING [AppendRope, New, TrustTextAsRope], Rope USING [Cat, Concat, Fetch, Find, IsEmpty, Length, ROPE, Size, Substr], RopeEdit USING [BlankChar], SirPress USING [ClosePress, Create, PressHandle, PutText, SetFont, SetPageSize, WritePage], TEditInput USING [FreeTree], TEditProfile USING [sourceExtensions], TextNode USING [Forward, NarrowToTextNode, Ref, RefTextNode], UserCredentials USING [Get], UserProfile USING [Token]; Print: CEDAR PROGRAM IMPORTS Commander, CommandTool, Convert, FS, IO, PressPrinter, Process, PutGet, Real, RefText, Rope, RopeEdit, SirPress, TEditInput, TEditProfile, TextNode, UserCredentials, UserProfile = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Micas: TYPE = INT; pressHandle: SirPress.PressHandle _ NIL; PressFromText: PROC [ text: --input-- STREAM, press: --output-- STREAM, headingFontFamily: ROPE, textFontFamily: ROPE, columnsPerPage: [1..100], pointSize: NAT, portrait: BOOL, shortFileName: ROPE, fileNameForLeaderPage: --only first 49 chars will show-- ROPE, fileNameForHeading: ROPE, pressFileName: ROPE, doNotClose: BOOL _ FALSE] RETURNS [pressPages: INT] = { pageHeightTenths: INT ~ IF portrait THEN 110 ELSE 85; -- tenth inches pageWidthTenths: INT ~ IF portrait THEN 85 ELSE 110; -- tenth inches pageWidth: Micas ~ pageWidthTenths*254; pageHeight: Micas ~ pageHeightTenths*254; topMargin: Micas ~ IF portrait THEN 2581 ELSE 1905; bottomMargin: Micas ~ IF portrait THEN 1270 ELSE 1905; leftMargin: Micas ~ IF portrait THEN 1905 ELSE 1270; rightMargin: Micas ~ 1270; interColumnSpace: Micas ~ 2540/columnsPerPage; columnOffset: Micas ~ (pageWidth-leftMargin-rightMargin+interColumnSpace)/columnsPerPage; columnWidthForText: Micas ~ columnOffset - interColumnSpace; xCoordinateOfLeftEdge: Micas ~ leftMargin; yCoordinateOfFirstBaseline: Micas ~ pageHeight - topMargin; yCoordinateOfPageNumberBaseline: Micas ~ bottomMargin/2; yCoordinateOfHeadingBaseline: Micas ~ pageHeight - topMargin/2; micasPerPoint: REAL ~ 2540.0 / 72.0; -- About 35.277777 charWidth: Micas ~ Real.RoundLI[pointSize*micasPerPoint*0.612]; deltaYCoordinateBetweenLines: Micas ~ Real.RoundLI[1.2*pointSize*micasPerPoint]; -- 1.2 times the design-size of the font maxCharsPerLine: NAT ~ columnWidthForText / charWidth; maxLinesPerColumn: NAT ~ (yCoordinateOfFirstBaseline - bottomMargin) / deltaYCoordinateBetweenLines; IF pressHandle = NIL THEN pressHandle _ SirPress.Create[press, fileNameForLeaderPage]; pressHandle.SetPageSize[height: pageHeightTenths, width: pageWidthTenths]; pressHandle.SetFont[family: textFontFamily, size: pointSize]; BEGIN column: INT; xCoordinateOfColumnLeftEdge: Micas; headingBuffer: REF TEXT _ RefText.New[maxCharsPerLine*columnsPerPage]; PrintHeading: PROC [] = { pressHandle.SetFont[family: headingFontFamily, size: pointSize]; headingBuffer.length _ 0; headingBuffer _ RefText.AppendRope[to: headingBuffer, from: "Page "]; headingBuffer _ Convert.AppendInt[to: headingBuffer, from: column]; pressHandle.PutText[textString: RefText.TrustTextAsRope[headingBuffer], xCoordinateOfLeftEdge: xCoordinateOfColumnLeftEdge+columnWidthForText-Real.RoundLI[pointSize*35.277777*0.612*headingBuffer.length], yCoordinateOfBaseline: yCoordinateOfPageNumberBaseline]; headingBuffer.length _ 0; IF (column-1) MOD columnsPerPage = 0 THEN BEGIN -- First column of page start: INT _ fileNameForHeading.Length[] - (maxCharsPerLine*columnsPerPage-shortFileName.Length-3); IF start > 0 THEN BEGIN headingBuffer _ RefText.AppendRope[to: headingBuffer, from: "..."]; start _ start + 3; END ELSE start _ 0; headingBuffer _ RefText.AppendRope[to: headingBuffer, from: fileNameForHeading, start: start]; pressHandle.PutText[textString: RefText.TrustTextAsRope[headingBuffer], xCoordinateOfLeftEdge: xCoordinateOfLeftEdge, yCoordinateOfBaseline: yCoordinateOfHeadingBaseline]; pressHandle.PutText[textString: shortFileName, xCoordinateOfLeftEdge: xCoordinateOfLeftEdge + columnsPerPage*columnOffset - interColumnSpace - Real.RoundLI[pointSize*35.277777*0.612*shortFileName.Length], yCoordinateOfBaseline: yCoordinateOfHeadingBaseline]; END; pressHandle.SetFont[family: textFontFamily, size: pointSize]; }; indent: BOOL _ FALSE; indentChars: NAT _ 0; lineBuffer: REF TEXT = RefText.New[maxCharsPerLine+1]; PrintColumn: PROC [] RETURNS [eof: BOOL] = { ff: BOOL; linesPrinted: INT _ 0; UNTIL linesPrinted = maxLinesPerColumn DO [eof, ff, indent, indentChars] _ FillLineBuffer[lineBuffer, maxCharsPerLine, indent, indentChars, text]; IF linesPrinted = 0 THEN BEGIN IF eof THEN RETURN [TRUE]; IF ff THEN LOOP; PrintHeading[]; END; IF lineBuffer.length > 0 THEN pressHandle.PutText[ RefText.TrustTextAsRope[lineBuffer], xCoordinateOfColumnLeftEdge, yCoordinateOfFirstBaseline - linesPrinted*deltaYCoordinateBetweenLines]; IF eof OR ff THEN RETURN [FALSE]; linesPrinted _ linesPrinted.SUCC; ENDLOOP; RETURN [FALSE]; }; FOR column _ 1, column _ column.SUCC DO xCoordinateOfColumnLeftEdge _ xCoordinateOfLeftEdge + ((column-1) MOD columnsPerPage)*columnOffset; IF PrintColumn[].eof THEN EXIT; IF column MOD columnsPerPage = 0 THEN pressHandle.WritePage[]; ENDLOOP; IF doNotClose THEN BEGIN pressHandle.WritePage[]; pressHandle.SetFont[family: textFontFamily, size: pointSize]; END ELSE pressHandle.ClosePress[]; RETURN [(column-1+columnsPerPage-1)/columnsPerPage] END; };--PressFromText FillLineBuffer: PROC [lineBuffer: REF TEXT, maxCharsPerLine: NAT, indent: BOOL, indentChars: NAT, text: --input-- STREAM] RETURNS [eof, ff: BOOL _ FALSE, nextIndent: BOOL _ FALSE, nextIndentChars: NAT _ 0] = BEGIN spacesPerTab: NAT = 4; extraIndentForFollowingLines: NAT = 2; lineLength, maxLineLength: NAT_ 0; OpenLine: PROC [] = BEGIN lineLength _ 0; maxLineLength _ IF indent THEN maxCharsPerLine-indentChars ELSE maxCharsPerLine; END; Append: PROC [c: CHAR] = BEGIN lineBuffer[lineLength] _ c; lineLength _ lineLength + 1 END; UnAppend: PROC [n: NAT] = BEGIN FOR i: NAT DECREASING IN [lineLength-n .. lineLength-1] DO text.Backup[lineBuffer[i]]; ENDLOOP; lineLength _ lineLength - n; END; IsTabStop: PROC [] RETURNS [BOOL] = BEGIN RETURN [ (IF indent THEN lineLength+indentChars ELSE lineLength) MOD spacesPerTab = 0]; END; IndexOfLastWhiteSpace: PROC [] RETURNS [n: NAT] = BEGIN FOR i: NAT DECREASING IN [1 .. lineLength) DO -- don't look at first char in line IF lineBuffer[i].ORD <= (IO.SP).ORD THEN RETURN [i]; ENDLOOP; RETURN [NAT.LAST] END; IndexOfFirstNonwhiteSpace: PROC [] RETURNS [n: NAT] = BEGIN FOR i: NAT IN [0 .. lineLength) DO IF lineBuffer[i].ORD > (IO.SP).ORD THEN RETURN [i]; ENDLOOP; RETURN [0]; END; CloseLine: PROC [] RETURNS [] = BEGIN IF indent THEN BEGIN FOR i: NAT DECREASING IN [0 .. lineLength) DO lineBuffer[i + indentChars] _ lineBuffer[i]; ENDLOOP; lineLength _ lineLength + indentChars; FOR i: NAT IN [0 .. indentChars) DO lineBuffer[i] _ IO.SP ENDLOOP; END; lineBuffer.length _ lineLength; END; OpenLine[]; DO BEGIN char: CHAR; BEGIN char _ text.GetChar[ ! IO.EndOfStream => GOTO endOfStream]; EXITS endOfStream => BEGIN char _ IO.CR; eof _ TRUE END END; IF eof AND lineLength = 0 THEN GOTO done; IF char = IO.FF THEN BEGIN IF lineLength = 0 THEN BEGIN ff _ TRUE; GOTO done END; text.Backup[char]; char _ IO.CR; END; IF char = IO.CR THEN GOTO done; IF char = IO.TAB THEN BEGIN THROUGH [0 .. spacesPerTab) DO Append[IO.SP]; IF IsTabStop[] THEN EXIT; IF lineLength > maxLineLength THEN EXIT; ENDLOOP; END ELSE Append[char]; IF lineLength > maxLineLength THEN BEGIN i: NAT _ IndexOfLastWhiteSpace[]; IF i = NAT.LAST THEN BEGIN UnAppend[3]; Append['~]; Append['~]; END ELSE BEGIN UnAppend[lineLength - (i+1)]; lineLength _ lineLength - 1; END; nextIndentChars _ IF indent THEN indentChars ELSE MIN[ IndexOfFirstNonwhiteSpace[] + extraIndentForFollowingLines, maxCharsPerLine/2]; nextIndent _ TRUE; GOTO done; END; EXITS done => BEGIN CloseLine[]; RETURN END END ENDLOOP; END;--FillLineBuffer PlainTextStreamFromNode: PROC [from: TextNode.Ref, to: STREAM] = BEGIN WritePlain[h: to, root: from, restoreDashes: TRUE]; to.Close[]; RETURN; END; PressFileFromFile: PROC [from: ROPE, pressStream: IO.STREAM, headingFontFamily: ROPE, textFontFamily: ROPE, pressFileName: ROPE, columnsPerPage: [1..100], pointSize: NAT, portrait: BOOL, ignoreNodes: BOOL, cmd: Commander.Handle, doNotClose: BOOL _ FALSE] RETURNS [ok: BOOL] = BEGIN fromStream: STREAM = FS.StreamOpen[from]; fromName, fromAttachedToName: ROPE; cp: FS.ComponentPositions; shortFileName, fileNameForLeaderPage, fileNameForHeading: ROPE; numBytes: INT; [fullFName: fromName, attachedTo: fromAttachedToName, bytes: numBytes] _ FS.FileInfo[from]; [shortFileName, cp] _ FS.ExpandName[fromName]; shortFileName _ shortFileName.Substr[cp.base.start, cp.ext.start+cp.ext.length-cp.base.start]; fileNameForLeaderPage _ fromName; fileNameForHeading _ IO.PutFR["%g of %g", IO.rope[IF fromAttachedToName.IsEmpty[] THEN fromName ELSE fromAttachedToName], IO.time[FS.FileInfo[from].created]]; BEGIN pressPages: INT; IF ignoreNodes OR fromStream.GetLength[] = numBytes THEN BEGIN cmd.out.PutF["Printing %g (text) ...", IO.rope[fromName]]; pressPages _ PressFromText[ text: fromStream, press: pressStream, headingFontFamily: headingFontFamily, textFontFamily: textFontFamily, columnsPerPage: columnsPerPage, pointSize: pointSize, portrait: portrait, shortFileName: shortFileName, fileNameForLeaderPage: fileNameForLeaderPage, fileNameForHeading: fileNameForHeading, pressFileName: pressFileName, doNotClose: doNotClose]; END ELSE BEGIN rootNode: TextNode.Ref; tempStream: STREAM _ FS.StreamOpen["[]<>Temp>Print.temp", $create]; cmd.out.PutF["Making %g plain text ...", IO.rope[fromName]]; rootNode _ PutGet.FromFile[fromName ! PutGet.FromFileError => BEGIN cmd.out.PutRope["Tioga error reading file\n"]; GOTO fail; END]; cmd.out.PutRope[" formatting ..."]; PlainTextStreamFromNode[from: rootNode, to: tempStream]; tempStream _ FS.StreamOpen["[]<>Temp>Print.temp"]; pressPages _ PressFromText[ text: tempStream, press: pressStream, headingFontFamily: headingFontFamily, textFontFamily: textFontFamily, columnsPerPage: columnsPerPage, pointSize: pointSize, portrait: portrait, shortFileName: shortFileName, fileNameForLeaderPage: fileNameForLeaderPage, fileNameForHeading: fileNameForHeading, pressFileName: pressFileName, doNotClose: doNotClose]; TEditInput.FreeTree[rootNode]; tempStream.Close[]; END; fromStream.Close[]; cmd.out.PutF[" %g %g\n", IO.int[pressPages], IO.rope[IF pressPages > 1 THEN "pages." ELSE "page."]]; RETURN [ok: TRUE] EXITS fail => BEGIN RETURN [ok: FALSE] END END END; SendPressFile: PROC [fileName, server: ROPE, copies: INT, cmd: Commander.Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] = BEGIN lastState: PressPrinter.State _ $aborted; aborted: BOOL _ FALSE; dotsPrinted: LONG CARDINAL _ 0; SendPressFileProgress: PROC [handle: PressPrinter.Handle] = BEGIN state: PressPrinter.State = handle.CurrentState[]; IF state = lastState THEN { cmd.out.PutChar['.]; dotsPrinted _ dotsPrinted + 1; IF dotsPrinted MOD 10 = 0 THEN cmd.out.PutChar[' ]; } ELSE BEGIN cmd.out.PutF["\n%g ", IO.rope[handle.CurrentStateMessage[]]]; IF state IN [$queued .. $serverTrouble] THEN cmd.out.PutRope["... "]; END; lastState _ state; BEGIN Process.CheckForAbort[ ! ABORTED => GOTO abortTransmission]; EXITS abortTransmission => BEGIN aborted _ TRUE; PressPrinter.Abort[handle] END; END; END;--SendPressFileProgress printerHandle: PressPrinter.Handle _ NIL; printerHandle _ PressPrinter.SendPressFile[ fileName: fileName, server: server, copies: copies, userName: UserCredentials.Get[].name, progressProc: SendPressFileProgress ! ABORTED => CONTINUE]; IF printerHandle = NIL OR printerHandle.CurrentState[] # $done THEN aborted _ TRUE; IF aborted THEN BEGIN cmd.out.PutRope["Aborting press file send... "]; RETURN [$Failure, NIL]; END; cmd.out.PutRope[".\n"]; END; FileCheck: PROC [fileName: ROPE, wDir: ROPE _ NIL] RETURNS [exists: BOOL, fullFName: ROPE] = BEGIN [fullFName: fullFName] _ FS.FileInfo[name: fileName, wDir: wDir ! FS.Error => IF error.group = $user THEN GOTO doesNotExist]; RETURN [exists: TRUE, fullFName: fullFName] EXITS doesNotExist => RETURN [exists: FALSE, fullFName: NIL]; END; CompleteFileName: PROC [fileName: ROPE, wDir: ROPE _ NIL] RETURNS [fullFName: ROPE] = BEGIN exists: BOOL; [exists, fullFName] _ FileCheck[fileName, wDir]; IF exists OR fileName.Find["!"] >= 0 OR fileName.Find["."] >= 0 THEN RETURN; FOR extList: LIST OF ROPE _ TEditProfile.sourceExtensions, extList.rest UNTIL extList = NIL DO f: ROPE = fileName.Cat[".", extList.first]; [exists, fullFName] _ FileCheck[f, wDir]; IF exists THEN RETURN; ENDLOOP; END; PressFileName: PROC [fileName: ROPE, newDir: ROPE] RETURNS [ROPE] = BEGIN cp: FS.ComponentPositions; base: ROPE; [fullFName: fileName, cp: cp] _ FS.ExpandName[name: fileName]; base _ fileName.Substr[cp.base.start, cp.base.length]; RETURN [Rope.Cat[newDir, base, ".press"]]; END; WritePlain: PUBLIC PROC [h: IO.STREAM, root: TextNode.Ref, restoreDashes: BOOL _ FALSE] = BEGIN HasInitialDashes: PROC [r: ROPE] RETURNS [BOOL] = BEGIN loc: INT _ 0; size: INT = Rope.Size[r]; c: CHAR; IF r = NIL THEN RETURN [FALSE]; WHILE loc < size AND RopeEdit.BlankChar[c _ Rope.Fetch[r, loc]] DO loc _ loc+1; ENDLOOP; IF loc > size OR c # '- OR Rope.Fetch[r, loc+1] # '- THEN RETURN [FALSE]; RETURN [TRUE]; END; node: TextNode.Ref _ root; level: INTEGER _ 0; levelDelta: INTEGER; first: BOOL _ TRUE; DO text: TextNode.RefTextNode; [node, levelDelta] _ TextNode.Forward[node]; IF node=NIL THEN EXIT; IF first THEN first _ FALSE ELSE IO.PutChar[h, '\n]; -- carriage returns between nodes level _ level+levelDelta; IF (text _ TextNode.NarrowToTextNode[node])=NIL THEN LOOP; THROUGH [1..level) DO IO.PutChar[h, '\t]; ENDLOOP; -- output level-1 tabs IF restoreDashes AND text.comment AND ~HasInitialDashes[text.rope] THEN IO.PutRope[h, "-- "]; -- restore the leading dashes for Mesa comments IO.PutRope[h, text.rope]; ENDLOOP; BEGIN ENABLE IO.Error => IF ec = NotImplementedForThisStream THEN GOTO Exit; IO.SetLength[h, IO.GetIndex[h]] END EXITS Exit => RETURN END; DoPrint: Commander.CommandProc = BEGIN argv: CommandTool.ArgumentVector _ NIL; i: NAT; wDir: ROPE _ CommandTool.CurrentWorkingDirectory[]; fileName: ROPE; columnsPerPage: INT _ -1; pointSize: INT _ -1; portrait: BOOL _ FALSE; ignoreNodes: BOOL _ FALSE; nCopies: INT _ 1; host: ROPE _ UserProfile.Token[key: "Hardcopy.PressPrinter", default: NIL]; hostSpecified: BOOL _ NOT host.IsEmpty[]; pressFileName: ROPE _ NIL; retainPressFile: BOOL _ FALSE; headingFontFamily: ROPE _ "Gacha"; textFontFamily: ROPE _ "Gacha"; fileRef: TYPE ~ REF fileRec; fileRec: TYPE ~ RECORD [ next: fileRef _ NIL, file: ROPE _ NIL ]; fileHead: fileRef _ NIL; fileTail: fileRef _ NIL; patternHead: fileRef _ NIL; patternTail: fileRef _ NIL; pressHandle _ NIL; BEGIN -- (a) interpret the command line, modifying fileName, ... , retainPressFile. IsParm: PROC [i: NAT] RETURNS [BOOL] = BEGIN RETURN [i # argv.argc-1 AND NOT argv[i].IsEmpty[] AND argv[i].Fetch[0] # '-]; END; cmd.commandLine _ Rope.Cat[UserProfile.Token["Print.DefaultOptions"], " ", cmd.commandLine]; argv _ CommandTool.Parse[cmd ! CommandTool.Failed => BEGIN msg _ errorMsg; CONTINUE; END]; IF argv = NIL THEN RETURN[$Failure, msg]; IF argv.argc < 2 THEN RETURN[$Failure, printHelpText]; FOR i _ 1, i.SUCC UNTIL i = argv.argc DO IF argv[i].Length[] = 2 AND argv[i].Fetch[0] = '- THEN BEGIN SELECT argv[i].Fetch[1] FROM 'p => portrait _ TRUE; 'l => portrait _ FALSE; 'f => { IF i = argv.argc - 1 THEN RETURN[$Failure, "No text font specified\n"]; i _ i.SUCC; textFontFamily _ argv[i]; }; 'F => { IF i = argv.argc - 1 THEN RETURN[$Failure, "No heading font specified\n"]; i _ i.SUCC; headingFontFamily _ argv[i]; }; 'n => { IF i = argv.argc - 1 THEN RETURN[$Failure, "No column count specified\n"]; i _ i.SUCC; columnsPerPage _ Convert.IntFromRope[argv[i] ! Convert.Error => GOTO numberSyntaxError]; IF columnsPerPage NOT IN [1..100] THEN GOTO outOfRange; }; 's => { IF i = argv.argc - 1 THEN RETURN[$Failure, "No point size specified\n"]; i _ i.SUCC; pointSize _ Convert.IntFromRope[argv[i] ! Convert.Error => GOTO numberSyntaxError]; IF pointSize NOT IN [4..72] THEN GOTO outOfRange; }; 't => ignoreNodes _ TRUE; 'c => BEGIN IF i = argv.argc - 1 THEN RETURN[$Failure, "No copy numbers present\n"]; i _ i.SUCC; nCopies _ Convert.IntFromRope[argv[i] ! Convert.Error => GOTO numberSyntaxError]; END; 'h => BEGIN hostSpecified _ TRUE; IF i = argv.argc - 1 THEN host _ NIL ELSE IF IsParm[i.SUCC] THEN BEGIN i _ i.SUCC; host _ argv[i] END ELSE host _ NIL; END; 'r => BEGIN retainPressFile _ TRUE; IF i = argv.argc - 1 THEN pressFileName _ NIL ELSE IF IsParm[i.SUCC] THEN BEGIN i _ i.SUCC; pressFileName _ argv[i] END ELSE pressFileName _ NIL; END; ENDCASE => GOTO switchSyntaxError; LOOP; END; IF patternTail = NIL THEN patternHead _ patternTail _ NEW[fileRec] ELSE {patternTail.next _ NEW[fileRec]; patternTail _ patternTail.next; }; patternTail.file _ argv[i]; ENDLOOP; IF pointSize < 0 THEN pointSize _ IF portrait THEN 8 ELSE 6; IF columnsPerPage < 0 THEN columnsPerPage _ IF portrait THEN 1 ELSE 2; EXITS switchSyntaxError => RETURN[ $Failure, IO.PutFR["Unrecognized switch: \"%g\"\n", IO.rope[argv[i]]]]; numberSyntaxError => RETURN[ $Failure, IO.PutFR["Unrecognized number: \"%g\"\n", IO.rope[argv[i]]]]; outOfRange => RETURN[ $Failure, IO.PutFR["Number out of range: \"%g\"\n", IO.rope[argv[i]]]]; END; -- (a) BEGIN -- Validate fileName (add file extension if not specified) and check hostSpecified NameProc: FS.NameProc = BEGIN -- PROC [fullFName: ROPE] RETURNS [continue: BOOLEAN]; ValidateName [fullFName]; RETURN [TRUE]; END; ValidateName: PROC [fullFName: ROPE] = BEGIN cp: FS.ComponentPositions; full: ROPE; ext: ROPE; ok: BOOL _ TRUE; [fullFName: full, cp: cp] _ FS.ExpandName[fullFName]; ext _ full.Substr[cp.ext.start, cp.ext.length]; IF NOT ok THEN RETURN; IF fileTail = NIL THEN fileHead _ fileTail _ NEW[fileRec] ELSE {fileTail.next _ NEW[fileRec]; fileTail _ fileTail.next; }; fileTail.file _ fullFName; RETURN; END; patternTail _ patternHead; IF patternTail = NIL THEN RETURN[$Failure, "No file or pattern specified.\n"]; DO fileName _ patternTail.file; IF fileName.Find["*"] < 0 THEN BEGIN name: ROPE _ CompleteFileName[fileName]; IF name # NIL THEN ValidateName[name]; END ELSE BEGIN IF fileName.Find["!"] < 0 THEN fileName _ fileName.Concat["!h"]; FS.EnumerateForNames[fileName, NameProc, wDir]; END; IF patternTail.next = NIL THEN EXIT; patternTail _ patternTail.next; ENDLOOP; IF NOT hostSpecified THEN RETURN[$Failure, "No print server specified in user profile or with \"-h\"\n"]; IF host.IsEmpty THEN retainPressFile _ TRUE; END; BEGIN -- do the real work exists: BOOL; fileName: ROPE; closeIt: BOOL; BEGIN ENABLE BEGIN IO.Error => BEGIN error: FS.ErrorDesc; IF ec # $Failure THEN GOTO cantHandle; error _ FS.ErrorFromStream[stream ! IO.Error => GOTO cantHandle]; cmd.out.PutF["FS error: %g\n", IO.rope[error.explanation]]; GOTO cleanupAfterError; EXITS cantHandle => REJECT END; FS.Error => BEGIN cmd.out.PutF["FS error: %g\n", IO.rope[error.explanation]]; GOTO cleanupAfterError; END; ABORTED => BEGIN GOTO cleanupAfterError; END; END; IF fileHead = NIL THEN RETURN[$Failure, "No files from enumeration.\n"]; fileTail _ fileHead; fileName _ fileHead.file; closeIt _ fileHead.next # NIL; IF fileHead.next = NIL THEN -- can only send 1 press file. IF PressPrinter.IsAPressFile[fileName] THEN BEGIN cmd.out.PutF["File %g is already in press format\n", IO.rope[fileName]]; pressFileName _ fileName; retainPressFile _ TRUE; END ELSE BEGIN pressStream: IO.STREAM; IF pressFileName.IsEmpty[] THEN pressFileName _ PressFileName[ fileName, IF retainPressFile THEN "" ELSE "[]<>Temp>"]; pressStream _ FS.StreamOpen[fileName: pressFileName, accessOptions: $create, keep: IF retainPressFile THEN 2 ELSE 1]; IF NOT PressFileFromFile[from: fileName, pressStream: pressStream, headingFontFamily: headingFontFamily, textFontFamily: textFontFamily, pressFileName: pressFileName, columnsPerPage: columnsPerPage, pointSize: pointSize, portrait: portrait, ignoreNodes: ignoreNodes, cmd: cmd, doNotClose: closeIt] THEN GOTO cleanupAfterError; pressStream.Close[]; END ELSE BEGIN pressStream: IO.STREAM; IF pressFileName.IsEmpty[] THEN pressFileName _ PressFileName[ fileName, IF retainPressFile THEN "" ELSE "[]<>Temp>"]; pressStream _ FS.StreamOpen[fileName: pressFileName, accessOptions: $create, keep: IF retainPressFile THEN 2 ELSE 1]; DO IF PressPrinter.IsAPressFile[fileName] THEN BEGIN cmd.out.PutF["File %g is already in press format.\nI can send only one at a time\n", IO.rope[fileName]]; GOTO cleanupAfterError; END; IF NOT PressFileFromFile[from: fileName, pressStream: pressStream, headingFontFamily: headingFontFamily, textFontFamily: textFontFamily, pressFileName: pressFileName, columnsPerPage: columnsPerPage, pointSize: pointSize, portrait: portrait, ignoreNodes: ignoreNodes, cmd: cmd, doNotClose: closeIt] THEN GOTO cleanupAfterError; IF fileTail.next = NIL THEN EXIT; fileTail _ fileTail.next; fileName _ fileTail.file; closeIt _ fileTail.next # NIL; ENDLOOP; pressStream.Close[]; END; IF NOT host.IsEmpty[] THEN [result, msg] _ SendPressFile[pressFileName, host, nCopies, cmd]; IF result # NIL THEN GOTO cleanupAfterError; IF NOT retainPressFile THEN BEGIN cmd.out.PutF["Deleting file %g ... ", IO.rope[pressFileName]]; FS.Delete[pressFileName]; cmd.out.PutChar['\n]; END; [exists: exists] _ FileCheck["[]<>Temp>Print.temp"]; IF exists THEN BEGIN cmd.out.PutRope["Deleting file []<>Temp>Print.temp ... "]; FS.Delete["[]<>Temp>Print.temp"]; cmd.out.PutChar['\n]; END; cmd.out.PutRope["Finished Print.\n"]; RETURN [result: NIL, msg: NIL]; EXITS cleanupAfterError => BEGIN cmd.out.PutRope["Aborted Print.\n"]; RETURN [result: $Failure, msg: NIL]; END; END; END; END;--DoPrint printHelpText: ROPE = "Usage: Print [-p] [-n nCols] [-s sizeFont] [-f textFont] [-F headingFont] [-h [hostName]] [-r [pfName]] [-c nCopies] [-t] file -p portrait mode (default is landscape) -n nCols number of columns (default is 2 for landscape, 1 for portrait) -s sizeFont size of font in points (default is 6 for landscape, 8 for portrait) -f textFont font to use for the text of each page (default is Gacha) -F headingFont font to use for the heading of each page (default is Gacha) -h [hostName] name of printer, empty sends to no printer (default is Hardcopy.PressPrinter entry of user profile) -r [pfName] retain press file, naming it pfName if specified -c nCopies number of copies to print -t print text only, without indenting to show Tioga nodes The file extension defaults according to the Tioga.SourceFileExtensions entry of user profile, like the Open command. If the file is already in press format, it is simply sent to a printer. "; Commander.Register["Print", DoPrint, printHelpText]; END. 0Print.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last edited by MBrown on January 16, 1984 12:09 pm PST Last edited by Tim Diebert: June 17, 1985 12:11:09 pm PDT Last edited by Michael Plass, June 9, 1986 4:19:22 pm PDT Last edited by Sturgis, July 9, 1985 1:47:13 pm PDT Last edited by Pavel, February 18, 1986 3:37:52 pm PST February 18, 1986: Pavel removed the dependency on the TSetter that he put in before and made the font stuff work wrong. -- Improvements that would be nice: -- 1. allow several files to be printed with a single command. -- 2. recognize node formats that have extra lead in front (e.g. unit in Cedar.style), and insert a -- blank line. -- 3. Some form of synchronization with Tioga Save button (like the Compile command). -- ! IO.Error [$Failure] (FS errors) -- Does not close text or press streams. 0.612 is roughly the width of the characters in Gacha and Snail. To allow the user to set the font but also not depend upon the TSetter, we have to hard-wire the number in here. It would be a lot easier if the Imager could read TFM files, but it can't... -- lineBuffer is garbage on entry. lineBuffer.maxLength >= maxCharsPerLine. -- Returns eof = TRUE iff end of stream from text and lineBuffer is empty. -- Returns ff = TRUE iff form-feed char read from text and lineBuffer is empty. -- here only if breaking an all-nonwhite line (unlikely) -- here only if breaking an all-white line (very unlikely) -- Assert 0 <= lineLength <= maxLineLength -- Assert 0 < lineLength <= maxLineLength+1 -- line won't fit in buffer; must insert extra line break -- line won't break at white space; break at end and insert "~~" -- put back chars following the last white space; then discard the last white space -- Assert 0 < lineLength <= maxLineLength -- Writes a plain text representation of from and its descendants onto to, then Closes to. -- Writes a plain text representation of file onto a press file named pressFileName. -- Avoids producing internal Tioga document format if ignoreNodes and for plain text files. -- ! IO.Error[$Failure] (FS errors) -- Read plain text directly from from. -- Built Tioga tree structure from from, then fork a process to produce a plain text version. -- Sends press file, giving feedback on its progress to cmd and checking for process abort. -- "For:" name on break page will be that of the logged-in user. -- this proc is called again with state = $aborted; then ABORTED is raised by SendPressFile -- Returns exists: TRUE iff file exists. If exists then fullFName is filled in to make later lookup faster. -- Returns NIL if can't find a completion that corresponds to an existing file PROC [cmd: Commander.Handle] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL] Here is the place to check for bad extensions, should you want to. -- File error handing is done at this level Κ&˜šœ ™ Icodešœ Οmœ1™J™rJ™U—J˜šΟk ˜ Jšœ žœ!˜0Jšœ žœ:˜KJšœžœ!˜.Jšžœžœ€˜ˆJšžœžœ žœ žœgžœžœžœ˜ŸJšœ žœX˜jJšœžœ˜Jšœžœ˜'Jšœžœ ˜Jšœžœ$˜1Jšœžœ-žœ˜KJšœ žœ ˜Jšœ žœM˜[Jšœ žœ ˜Jšœ žœ˜&Jšœ žœ/˜=Jšœžœ˜Jšœ žœ ˜—J˜šΟbœžœž˜Jšžœ"žœžœž˜ΑJšžœžœžœ˜Jšžœžœžœžœ˜Jšœžœžœ˜J˜Jšœ$žœ˜(šΠbn œžœ˜Jš œΟc œžœ ‘ œžœ˜1Jšœžœ˜Jšœžœ˜Jšœ˜Jšœ žœ˜Jšœ žœ˜Jšœžœ˜Jšœ‘!œžœ˜>Jšœžœ˜Jšœžœ˜Jšœ žœžœ˜šžœžœ˜J™Jš‘$™$Jš‘(™(Jš œžœžœ žœžœ‘˜EJš œžœžœ žœžœ‘˜DJšœ'˜'Jšœ)˜)J˜Jšœžœ žœžœ˜3Jšœžœ žœžœ˜6Jšœžœ žœžœ˜4Jšœ˜J˜Jšœ.˜.KšœY˜YJšœ<˜Jšžœ˜—šžœ ˜ šžœž˜ Jšœ˜Jšœ=˜=Jšž˜—Jšžœ˜—Jšžœ-˜3Jšžœ˜——Jšœ‘˜—J˜š  œžœžœžœžœ˜AJš œžœžœ‘ œžœ˜7Jš žœ žœžœž œžœž˜[JšœL™LJšœ Οrœžœ£œ™JJšœ £œžœ£œ™OJšœžœ˜Jšœžœ˜&Jšœžœ˜"J˜š’œžœž˜Jšœ˜Jšœžœžœžœ˜PJšžœ˜—J˜š’œžœžœž˜Jšœ8˜8Jšžœ˜—J˜š’œžœžœž˜š žœžœž œžœ ž˜:Jšœ˜Jšžœ˜—Jšœ˜Jšžœ˜—J˜š ’ œžœžœžœž˜)šžœ˜Jš œžœžœžœ žœ˜N—Jšžœ˜—J˜š ’œžœžœžœž˜7š žœžœž œžœžœ‘#˜QJšžœžœžœžœžœžœžœ˜4Jšžœ˜—Jš‘8™8Jšžœžœžœ˜Jšžœ˜—J˜š ’œžœžœžœž˜;šžœžœžœž˜"Jšžœžœžœžœžœžœžœ˜3Jšžœ˜—Jš‘:™:Jšžœ˜ Jšžœ˜—J˜š’ œžœžœž˜%šžœžœž˜š žœžœž œžœž˜-J˜,Jšžœ˜—Jšœ&˜&Jšžœžœžœžœžœžœžœ˜BJšžœ˜—Jšœ˜Jšžœ˜—J˜J˜ šžœž˜Jš‘ œ‘™*Jšœžœ˜ šž˜Jšœžœžœ˜;Jš žœžœžœžœžœž˜7Jšžœ˜—Jšžœžœžœžœ˜)š žœžœžœžœž˜Jš žœžœžœžœžœžœ˜6Jšœ˜Jšœžœžœ˜ Jšžœ˜—Jš žœžœžœžœžœ˜šžœžœž˜šžœž˜ šžœž˜Jšœžœžœ˜Jšžœ žœžœ˜Jšžœžœžœ˜(Jšžœ˜—Jšž˜—Jšžœ˜—Jš‘ œ‘™+šžœžœž˜(Jšœ9™9Jšœžœ˜!šžœžœž˜šžœž˜ Jš‘@™@J˜&Jšž˜—šžœž˜ Jš‘S™SJšœ˜Jšœ˜Jšžœ˜——Jšœ)™)šœžœ˜Jšžœ ˜šžœ˜ Jšœ;˜;Jšœ˜——Jšœ žœ˜Jšžœ˜ Jšžœ˜—Jšžœ žœžœž˜,Jšžœžœ˜ —Jšžœ‘˜—J˜š ’œžœžœžœž˜FJšœ)£œ£œ£œ™ZJšœ-žœ˜3J˜ Jšžœ˜Jšžœ˜—J˜š’œžœžœžœžœžœžœžœ'žœ žœžœ%žœžœ˜ώJšžœžœž˜Jš‘)Πcr‘€ ‘™TJšœ6£ œ™[J™#Jšœ žœ˜)J˜Jšœžœ˜#Jšœžœ˜Jšœ:žœ˜?Jšœ žœ˜šœH˜HJšœ˜—Jšœžœ˜.Jšœ^˜^Jšœžœ ˜!šœ˜Jš žœžœžœžœ žœžœ"˜‰—šž˜Jšœ žœ˜šžœ žœ"˜3šžœž˜ Jšœ!£œ™&Jšœ'žœ˜:šœ˜Jšœΰ˜ΰ—Jšž˜—šžœž˜ Jšœ#£œ6™]Jšœ˜Jšœ ž œ+˜CJšœ)žœ˜<šœ#˜#Jšœž˜Jšœ.˜.Jšžœ˜ Jšžœ˜—Jšœ#˜#Jšœ8˜8Jšœ žœ"˜2šœ˜Jšœΰ˜ΰ—Jšœ˜Jšœ˜Jšžœ˜——Jšœ˜šœ˜Jšžœžœžœ žœ ˜K—Jšžœžœ˜Jš žœ žœžœžœž˜*Jšž˜—Jšžœ˜—J˜š’ œžœžœ žœ˜PJš žœ žœžœžœžœž˜4Jš‘œX™[Jš‘œ=™@Jšœ)˜)Jšœ žœžœ˜Jšœ žœžœ˜š’œžœ!ž˜AJšœ2˜2šžœžœ˜Kšœ˜Kšœ˜Kšžœ žœžœ˜3Kšœ˜—šžœž˜ Jšœ=˜=Jšžœžœžœ˜EJšžœ˜—Jšœ˜šž˜Jšœžœžœ˜<šžœž˜ Jšœ žœ˜Jšœ˜Jš‘9Πck‘™[Jšžœ˜—Jšžœ˜—Jšžœ‘˜—Jšœ%žœ˜)šœ+˜+J˜3J˜%Jšœ&žœžœ˜;—Jš žœžœžœ&žœ žœ˜Sšžœ žœž˜Jšœ0˜0Jšžœ žœ˜Jšžœ˜—Jšœ˜Jšžœ˜—J˜š  œž˜Jš œ žœž œžœ žœ žœž˜RJšœžœU™lšœžœ$˜?Jš œžœ žœžœžœ˜=—Jšžœ žœ˜+Jšžœžœ žœ žœ˜=Jšžœ˜—J˜š œž˜Jš œ žœž œžœ žœž˜DJšœ žœ@™NJšœžœ˜ Jšœ0˜0Jš žœžœžœžœžœ˜Lš žœ žœžœžœ/žœ žœž˜^Jšœžœ$˜+Jšœ)˜)Jšžœžœžœ˜Jšžœ˜—Jšžœ˜—J˜š   œžœ žœ žœžœžœž˜IJšœžœ˜Jšœžœ˜ Jšœ>˜>Jšœ6˜6Jšžœ$˜*Jšžœ˜—J˜š’ œžœžœžœžœ%žœžœž˜_š ’œžœžœžœžœž˜7Jšœžœ˜ Jšœžœ˜Jšœžœ˜Jš žœžœžœžœžœ˜šžœ žœ,ž˜BJšœ ˜ Jšžœ˜—Jš žœ žœžœžœžœžœ˜IJšžœžœ˜Jšžœ˜—J˜J˜Jšœžœ˜Jšœ žœ˜Jšœžœžœ˜šž˜J˜J˜,Jšžœžœžœžœ˜Jšžœžœ ž˜Jšžœžœ‘!˜:J˜Jšžœ*žœžœžœ˜:Jš žœ žœžœžœ‘˜Išžœžœžœž˜GJšžœ‘/˜E—Jšžœ˜Jšžœ˜—šž˜Jš žœžœ žœ"žœžœ˜FJšžœžœ ˜Jšž˜—Jšžœ ž˜Jšžœ˜—J˜J˜šŸœž˜&Jš žœžœ žœžœ žœžœ™NJšœ#žœ˜'Jšœžœ˜Jšœžœ&˜3Jšœ žœ˜Jšœžœ˜Jšœ žœ˜Jšœ ž œ˜Jšœ žœžœ˜Jšœ žœ˜Jšœžœ<žœ˜KJšœžœžœ˜)Jšœžœžœ˜Jšœžœžœ˜Jšœžœ ˜"Jšœžœ ˜J˜Jšœ žœžœ ˜šœ žœž˜J˜Jšœžœ˜Jšœžœž˜J˜—Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜J˜Jšœžœ˜šžœ‘.œ‘œ‘˜Tš ’œžœžœžœžœž˜,Jšžœžœžœžœ˜MJšžœ˜—J˜K˜\Jšœ5žœžœžœ˜[Jšžœžœžœžœ˜)Jšžœžœžœ˜6šžœ žœžœž˜(šžœžœ˜1šžœž˜ šžœž˜Jšœžœ˜Jšœžœ˜šœ˜Jšžœžœžœ'˜GJšœžœ˜ Jšœ˜Jšœ˜—šœ˜Jšžœžœžœ*˜JJšœžœ˜ Jšœ˜Jšœ˜—šœ˜Jšžœžœžœ*˜JJšœžœ˜ Jšœ@žœ˜XJš žœžœžœ žœžœ ˜7Jšœ˜—šœ˜Jšžœžœžœ(˜HJšœžœ˜ Jšœ;žœ˜SJš žœ žœžœ žœžœ ˜1Jšœ˜—Jšœžœ˜šœžœ˜ Jšžœžœžœ(˜HJšœžœ˜ Jšœ9žœ˜QJšžœ˜—šœžœ˜ Jšœžœ˜šžœ˜Jšžœž˜šžœ žœ˜Jšžœžœžœž˜*Jšžœžœ˜——Jšžœ˜—šœžœ˜ Jšœžœ˜šžœ˜Jšžœž˜šžœ žœ˜Jšžœžœžœž˜3Jšžœžœ˜——Jšžœ˜—Jšžœžœ˜#——Jšžœ˜Jšž˜—J˜šžœž˜Jšžœžœ ˜-Jšžœžœ-˜I—Jšœ˜Jšžœ˜—Jš žœžœ žœ žœžœ˜Jšœ žœžœžœ˜7—šœ ˜ JšžœCžœžœžœ˜g—Jšžœžœ€žœžœ˜ΖJšœ˜Jšž˜———šž ˜ Jšœ ž ˜šžœž˜šœ˜Jšœ žœžœžœ˜7——šœžœ<˜LJšœžœžœžœ˜(—šž˜šžœ$˜&šžœž˜ JšœUžœ˜hJšžœ˜Jšž˜—J˜—Jšžœžœ€žœžœ˜ΖJ˜Jšžœžœžœžœ˜!Jšœ˜Jšœ˜Jšœžœ˜Jšžœ˜—Jšœ˜Jšžœ˜——J˜šžœžœž˜J˜A—Jšžœ žœžœžœ˜,šžœžœžœž˜!Jšœ&žœ˜>Jšžœ˜Jšœ˜Jšžœ˜—Jšœ4˜4šžœžœž˜Jšœ:˜:Jšžœ˜!Jšœ˜Jšžœ˜—J˜%Jšžœ žœžœ˜šž˜šœž˜Jšœ$˜$Jšžœžœ˜$Jšžœ˜——Jšžœ˜—Jšžœ˜—Jšžœ‘ ˜ —J˜J˜J˜Jš+œžœΟtœ¦œ¦œ¦œ¦ œ¦ œ¦œ¦œ¦œ¦œ¦œ¦œ¦ œ¦œ¦œ¦œ.¦œ¦œD¦œ¦œΊ˜ΤJšœ4˜4—Jšžœ˜—…—[Φ†,