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.UnpackedsicTime.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.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[]];
};
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[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;
};