<> <> <> DIRECTORY BasicTime, BitmapViewer, Commander, DefaultRemoteNames, FS, ImagerPixelMap, IO, MessageWindow, MFExternals, MFFileNames, MFInput, MFInteraction, MFRest, MFTypes, PascalBasic, PascalWizardFiles, PrincOps, PrincOpsUtils, Process, Rope, UserProfile, ViewerClasses, ViewerOps; MFExternalsImpl: PROGRAM IMPORTS PascalBasic, PascalWizardFiles, FS, IO, Rope, UserProfile, BasicTime, Process, Commander, MessageWindow, ImagerPixelMap, BitmapViewer, MFFileNames, MFInput, MFInteraction, MFRest, DefaultRemoteNames, ViewerOps EXPORTS MFExternals = BEGIN OPEN PascalBasic, PascalWizardFiles, MFTypes, MFInteraction, MFRest; RopeFromNameOfFile: PROCEDURE [] RETURNS [fileName: Rope.ROPE] = { i: NAT _ 0; P: SAFE PROC RETURNS [CHAR] = TRUSTED {i _ i+1; RETURN[NameOfFile[i]]}; length: NAT _ 0; WHILE length < FileNameSize AND NameOfFile[length+1] # ' DO length _ length+1; ENDLOOP; fileName _ Rope.FromProc[length, P]; IF i#length THEN ERROR; }; NameOfFileFromRope: PRIVATE PROCEDURE [r: Rope.ROPE] = { NameLength _ MIN[r.Length[], FileNameSize]; FOR i: [1..FileNameSize] IN [1..NameLength] DO NameOfFile[i]_r.Fetch[i-1] ENDLOOP; FOR i: [1..FileNameSize] IN (NameLength..NAT[FileNameSize]] DO NameOfFile[i] _ ' ; ENDLOOP; }; DefaultInputsDirectory: PROCEDURE RETURNS [dir: Rope.ROPE] = { dir _ UserProfile.Token["MF.DefaultInputsDirectory"]; IF dir = NIL THEN dir _ Rope.Cat[ DefaultRemoteNames.Get[].systemHost, "Inputs>" ]; }; DefaultBaseFile: PROCEDURE RETURNS [name: Rope.ROPE] = { name _ UserProfile.Token["MF.DefaultBaseFile"]; IF name = NIL THEN name _ Rope.Cat[ DefaultRemoteNames.Get[].systemHost, "Formats>Plain.base" ]; }; ReadProfileForDirectories: PUBLIC PROCEDURE = { inputs: Rope.ROPE _ DefaultInputsDirectory[]; IF inputs # NIL THEN { NameOfFileFromRope[inputs]; MFFileNames.MfArea _ MFFileNames.MakeNameString[]; }; }; SetPoolName: PUBLIC PROCEDURE = { poolName: Rope.ROPE = Rope.Cat[ DefaultInputsDirectory[], "MF.pool" ]; FOR i:NAT IN [0 .. NAT[poolName.Length]) DO NameOfFile[i+1] _ poolName.Fetch[i]; ENDLOOP; FOR i:NAT IN [NAT[poolName.Length] .. NAT[FileNameSize]) DO NameOfFile[i+1] _ ' ; ENDLOOP; }; ResetTermIn: PUBLIC PROCEDURE [F: LONG POINTER TO AlphaFile] = { PascalOpenTextFileTTYInput[F]; }; RewriteTermOut: PUBLIC PROCEDURE [F: LONG POINTER TO AlphaFile] = { PascalOpenTextFileTTYOutput[F]; }; ReadTheClock: PUBLIC PROCEDURE [Ttime, Dday, Mmonth, Yyear: LONG POINTER TO INT] = { now: BasicTime.Unpacked_BasicTime.Unpack[BasicTime.Now[]]; Ttime^ _ now.hour*BasicTime.minutesPerHour+now.minute; Dday^ _ now.day; Mmonth^ _ SELECT now.month FROM January=>1, February=>2, March=>3, April=>4, May=>5, June=>6, July=>7, August=>8, September=>9, October=>10, November=>11, December=>12, ENDCASE=>ERROR; Yyear^ _ now.year; }; AOpenIn: PUBLIC PROCEDURE [F: LONG POINTER TO AlphaFile] RETURNS [AOpenInResult: PascalBoolean] = { inStream: IO.STREAM; AOpenInResult _ TRUE; inStream _ FS.StreamOpen[RopeFromNameOfFile[] ! FS.Error => TRUSTED {Post[error]; AOpenInResult _ FALSE; GO TO Quit} ]; PascalOpenTextFileWithStream[F, inStream]; PascalTextRESET[F]; EXITS Quit => NULL }; AOpenOut: PUBLIC PROCEDURE [F: LONG POINTER TO AlphaFile] RETURNS [AOpenOutResult: PascalBoolean] = { outStream: IO.STREAM; AOpenOutResult _ TRUE; outStream _ FS.StreamOpen[RopeFromNameOfFile[], $create ! FS.Error => TRUSTED {Post[error]; AOpenOutResult _ FALSE; GO TO Quit} ]; PascalOpenTextFileWithStream[F, outStream]; PascalTextREWRITE[F]; EXITS Quit => NULL }; BOpenIn: PUBLIC PROCEDURE [F: LONG POINTER TO ByteFile] RETURNS [BOpenInResult: PascalBoolean] = { inStream: IO.STREAM; so: FS.StreamOptions _ FS.defaultStreamOptions; so[tiogaRead] _ FALSE; BOpenInResult _ TRUE; inStream _ FS.StreamOpen[fileName: RopeFromNameOfFile[], streamOptions: so ! FS.Error => TRUSTED {Post[error]; BOpenInResult _ FALSE; GO TO Quit} ]; PascalOpenFileWithStream[@(F^.baseFile), inStream]; PascalRESET[file: @(F^.baseFile), length: 1, element: @(F^.element)]; EXITS Quit => NULL }; BOpenOut: PUBLIC PROCEDURE [F: LONG POINTER TO ByteFile] RETURNS [BOpenOutResult: PascalBoolean] = { outStream: IO.STREAM; BOpenOutResult _ TRUE; outStream _ FS.StreamOpen[RopeFromNameOfFile[], $create ! FS.Error => TRUSTED {Post[error]; BOpenOutResult _ FALSE; GO TO Quit} ]; PascalOpenFileWithStream[@(F^.baseFile), outStream]; EXITS Quit => NULL }; WOpenIn: PUBLIC PROCEDURE [F: LONG POINTER TO WordFile] RETURNS [WOpenInResult: PascalBoolean] = { inStream: IO.STREAM; so: FS.StreamOptions _ FS.defaultStreamOptions; so[tiogaRead] _ FALSE; WOpenInResult _ TRUE; inStream _ FS.StreamOpen[fileName: RopeFromNameOfFile[], streamOptions: so ! FS.Error => TRUSTED {Post[error]; WOpenInResult _ FALSE; GO TO Quit} ]; PascalOpenFileWithStream[@(F^.baseFile), inStream]; PascalRESET[file: @(F^.baseFile), length: 4, element: @(F^.element)]; EXITS Quit => NULL }; WOpenOut: PUBLIC PROCEDURE [F: LONG POINTER TO WordFile] RETURNS [WOpenOutResult: PascalBoolean] = { outStream: IO.STREAM; WOpenOutResult _ TRUE; outStream _ FS.StreamOpen[RopeFromNameOfFile[], $create ! FS.Error => TRUSTED {Post[error]; WOpenOutResult _ FALSE; GO TO Quit} ]; PascalOpenFileWithStream[@(F^.baseFile), outStream]; EXITS Quit => NULL }; Post: PRIVATE PROCEDURE [e: FS.ErrorDesc] = { IF e.group # user THEN { MessageWindow.Append[message: e.explanation, clearFirst: TRUE]; MessageWindow.Blink; }; }; AClose: PUBLIC PROCEDURE [F: LONG POINTER TO AlphaFile] = { PascalCloseTextFile[F]; }; BClose: PUBLIC PROCEDURE [F: LONG POINTER TO ByteFile] = { PascalCloseFile[@(F^.baseFile)]; }; WClose: PUBLIC PROCEDURE [F: LONG POINTER TO WordFile] = { PascalCloseFile[@(F^.baseFile)]; }; AMakeNameString: PUBLIC PROCEDURE [F: LONG POINTER TO AlphaFile] RETURNS [AMakeNameStringResult: StrNumber] = {RETURN[MakeNameStringFromFileStream[F^.baseFile^.str]]}; BMakeNameString: PUBLIC PROCEDURE [F: LONG POINTER TO ByteFile] RETURNS [BMakeNameStringResult: StrNumber] = {RETURN[MakeNameStringFromFileStream[F^.baseFile^.str]]}; WMakeNameString: PUBLIC PROCEDURE [F: LONG POINTER TO WordFile] RETURNS [WMakeNameStringResult: StrNumber] = {RETURN[MakeNameStringFromFileStream[F^.baseFile^.str]]}; MakeNameStringFromFileStream: PRIVATE PROCEDURE [s: IO.STREAM] RETURNS [StrNumber] = {local, global, name: Rope.ROPE; [fullFName: local, attachedTo: global] _ FS.GetName[FS.OpenFileFromStream[s]]; IF global = NIL THEN name _ local ELSE name _ global; NameOfFileFromRope[name]; RETURN[MFFileNames.MakeNameString[]]}; FileGetPos: PUBLIC PROC [F: PascalTextFilePtr] RETURNS [FileGetPosResult: PascalInteger] = BEGIN FileGetPosResult _ 0; -- in case of error below FileGetPosResult _ F.baseFile.str.GetIndex[ ! IO.Error => IF ec = NotImplementedForThisStream THEN CONTINUE]; END; RopeFromStringNumber: PROCEDURE [s: StrNumber] RETURNS [r: Rope.ROPE] = { i: PoolPointer _ StrStart^[s]; length: NAT _ StrStart^[s+1]-i; P: SAFE PROC RETURNS [c:CHAR] = TRUSTED {c _ Xchr[StrPool[i]]; i _ i+1}; r _ Rope.FromProc[length, P]; RETURN[r]; }; SetNormalPriority: PUBLIC PROCEDURE = { Process.SetPriority[Process.priorityNormal]; }; SetBackgroundPriority: PUBLIC PROCEDURE = { Process.SetPriority[Process.priorityBackground]; }; <<>> < {command line was all blank}; 1=> {command line had non-blank stuff, and it has been loaded into the buffer}; -1=> {command line had so much non-blank stuff that it overflowed the buffer (which is unlikely, but we are running with bounds checks off, so it pays to be careful)}.>> StuffOnCmdLine: PUBLIC PROCEDURE RETURNS [StuffOnCmdLineResult: INT] = { tail: Rope.ROPE _ PascalBasic.commandLineTail; cmdStr: IO.STREAM _ IO.RIS[tail]; curChar: CHAR; myLoc: NAT _ First; lastNonBlank: NAT; DO curChar _ '\n; -- in case of EndOfStream curChar _ cmdStr.GetChar[! IO.EndOfStream => CONTINUE]; SELECT curChar FROM ' => LOOP; -- ignore leading whitespace '\n => RETURN[0]; -- everything was blank ENDCASE => EXIT; -- aha, a nonblank ENDLOOP; IF myLoc >= BufSize-1 THEN RETURN[-1]; Buffer[myLoc] _ Xord[curChar]; myLoc _ myLoc+1; lastNonBlank _ myLoc; DO curChar _ '\n; -- in case of EndOfStream curChar _ cmdStr.GetChar[! IO.EndOfStream => CONTINUE]; IF curChar = '\n THEN EXIT; IF myLoc >= BufSize-1 THEN RETURN[-1]; Buffer[myLoc] _ Xord[curChar]; myLoc _ myLoc+1; IF curChar # ' THEN lastNonBlank _ myLoc; ENDLOOP; Last _ lastNonBlank; MFInput.CurInput.LocField _ First; RETURN[1]; }; RopeFromBuffer: PRIVATE PROCEDURE [A: INT, B: INT] RETURNS [Rope.ROPE]= { i: NAT _ A-1; P: SAFE PROC RETURNS [CHAR] = TRUSTED {i _ i+1; RETURN[Xchr[Buffer[i]]]}; RETURN[Rope.FromProc[B-A+1, P]]; }; PackBufferedName: PUBLIC PROCEDURE [A: INT, B: INT] = { NameOfFileFromRope[Rope.Cat[RopeFromBuffer[A,B], ".base"]]; }; PackDefaultArea: PUBLIC PROCEDURE [A: INT, B: INT] = { cp: FS.ComponentPositions; fullName: Rope.ROPE; [fullName, cp] _ FS.ExpandName[DefaultBaseFile[]]; NameOfFileFromRope[ Rope.Cat[fullName.Substr[start:0, len: cp.base.start], RopeFromBuffer[A,B], ".base"]]; }; PackAllDefault: PUBLIC PROCEDURE = { NameOfFileFromRope[DefaultBaseFile[]]; }; <<>> <> feedbackViewer: ViewerClasses.Viewer; feedbackPixels: ImagerPixelMap.PixelMap; InitScreen: PUBLIC PROCEDURE RETURNS[InitScreenResult: PascalBoolean] = { feedbackViewer _ BitmapViewer.Create[[name: "Metafont Feedback", iconic: FALSE]]; feedbackPixels _ ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: [0, 0, 700, 1000]]; feedbackPixels.Clear[]; BitmapViewer.SetBitmap[feedbackViewer, feedbackPixels]; InitScreenResult _ TRUE; }; UpdateScreen: PUBLIC PROCEDURE = { BitmapViewer.TouchUp[feedbackViewer, [0, 0, 700, 1000]]; }; BlankRectangle: PUBLIC PROCEDURE [LeftCol, RightCol: ScreenCol, TopRow, BotRow: ScreenRow] = { feedbackPixels.Fill[area: [TopRow, LeftCol, BotRow - TopRow, RightCol - LeftCol], value: 0, function: [null, null]]; }; PaintRow: PUBLIC PROCEDURE [R: ScreenRow, B: PixelColor, A: LONG POINTER TO TransSpec, N: ScreenCol] = { FOR i : ScreenCol IN [0 .. N) DO feedbackPixels.Fill[area: [R, A^[i], 1, A^[i+1] - A^[i]], value: B]; B _ 1 - B; ENDLOOP; }; RegisterMfCommands: PUBLIC PROCEDURE = { PascalBasic.SubsystemProcRec.p _ MForINIMFDependingUponClientData; Commander.Register[key: "MF", proc: PascalBasic.ExclusiveProc, doc: "Runs METAFONT84, a font-generator", clientData: NEW[BOOL_FALSE], interpreted: FALSE]; Commander.Register[key: "INIMF", proc: PascalBasic.ExclusiveProc, doc: "Runs special initialising version of METAFONT84, a font-generator", clientData: NEW[BOOL_TRUE], interpreted: FALSE]; Commander.Register[key: "InterruptMF", proc: InterruptMF, doc: "Asks METAFONT84 to pause"]; }; MForINIMFDependingUponClientData: PascalBasic.UnsafeCommandProc = { actLikeINIMF: REF BOOL _ NARROW[PascalBasic.clientData]; StartLikeInimf _ actLikeINIMF^; TheRealMf; IF feedbackViewer # NIL THEN ViewerOps.DestroyViewer[feedbackViewer]; }; InterruptMF: Commander.CommandProc = CHECKED { Interrupt _ 1; }; END.