file: MFExternalsImpl.mesa
Stolen from TeXSysDepImpl.mesa and munged to work with METAFONT84 by Pavel.
Last Edited by Pavel on September 12, 1985 3:09:13 pm PDT
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, "<TeX>Inputs>" ];
};
DefaultBaseFile: PROCEDURE RETURNS [name: Rope.ROPE] = {
name ← UserProfile.Token["MF.DefaultBaseFile"];
IF name = NIL THEN
name ← Rope.Cat[ DefaultRemoteNames.Get[].systemHost, "<TeX>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�sicTime.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];
};
The following procedure reads the command line and returns one of three different values, encoded as integers: 0=> {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.STREAMIO.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: NATA-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[]];
};
This next set of procedures implements the primitive screen operations required by METAFONT.
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[BOOLFALSE],
interpreted: FALSE];
Commander.Register[key: "INIMF",
proc: PascalBasic.ExclusiveProc,
doc: "Runs special initialising version of METAFONT84, a font-generator",
clientData: NEW[BOOLTRUE],
interpreted: FALSE];
Commander.Register[key: "InterruptMF", proc: InterruptMF,
doc: "Asks METAFONT84 to pause"];
};
MForINIMFDependingUponClientData: PascalBasic.UnsafeCommandProc = {
actLikeINIMF: REF BOOLNARROW[PascalBasic.clientData];
StartLikeInimf ← actLikeINIMF^;
TheRealMf;
IF feedbackViewer # NIL THEN
ViewerOps.DestroyViewer[feedbackViewer];
};
InterruptMF: Commander.CommandProc = CHECKED {
Interrupt ← 1;
};
END.