-- MDMainImpl.mesa
-- last edit by Schmidt, January 14, 1983 10:27 am
-- last edit by Satterthwaite, February 9, 1983 12:50 pm
-- procedures for the system modeller
					
DIRECTORY
  ConvertUnsafe: TYPE USING [ToRope],
  CWF: TYPE USING [FWF0, FWF1, FWF2, FWF3, SetCode, SWF1, WF0, WF1, WF2, WF4],
  DBStash: TYPE USING [ForceOut, GetNHits, GetNLeaders, SetNHits, SetNLeaders],
  Dir: TYPE USING [FileInfo, NewVersion],
  Directory: TYPE USING [Error, Handle, ignore, Lookup],
  File: TYPE USING [Capability],
  IO: TYPE USING [Handle, Flush, Put, PutChar, PutF, rope, string],
  LongString: TYPE USING [EquivalentString],
  MDComp: TYPE USING [DetermineRecomp, NewBind],
  MDDB: TYPE USING [BringOverFilesAndCheckAllParms, CheckAndFillInParameters],
  MDLoad: TYPE USING [LoadBcdsAndResolveImports, StartAllControlBcds, UnLoad],
  MDMain: TYPE USING [Transaction],
  MDModel: TYPE USING [
    AllocateSymbolSeq, FreeSymbolSeq, GetFileInfo, GetSrcCreate, LOCSymbol, ModelParse,
    MODELSymbol, NarrowToLIST, NarrowToLOC, numberofbcdsmapped, numberofsourcesparsed,
    ParseInit, ProcessFilename, StartMDSupport, StopMDSupport, StopScanner, Symbol, SymbolSeq,
    TraverseList, TraverseTree, traversetreecalled, ValidateModel],
  MDUtil: TYPE USING [AcquireMsgLock, PrintNewModelStream, ReleaseMsgLock, SupportInit],
  MoveFiles: TYPE USING [InternalPermanentOrTemporary],
  Process: TYPE USING [Detach, Yield],
  Rope: TYPE USING [ROPE, Text],
  RopeInline: TYPE USING [InlineFlatten],
  STPSubr: TYPE USING [StopSTP],
  Subr: TYPE USING [
    AbortMyself, debugflg, errorflg, GetCreateDate, LongZone,
    numberofleaders, strcpy, SubrStop, SubStrCopy, TTYProcs],
  Time: TYPE USING [Current],
  TypeScript: TYPE USING [TS, UserAbort],
  UserExec: TYPE USING [CommandProc, RegisterCommand, UserAbort],
  ViewerClasses: TYPE USING [Viewer],
  ViewerEvents: TYPE USING [
    EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc];

MDMainImpl: MONITOR
IMPORTS ConvertUnsafe, CWF, DBStash, Dir, Directory, IO, LongString, MDComp, MDDB, 
	MDLoad, MDModel, MDUtil, MoveFiles, Process, RopeInline, 
	STPSubr, Subr, Time, TypeScript, UserExec, ViewerEvents
EXPORTS MDMain = {

maxsym: NAT = 7000;	-- the number of symbols

-- MDS Usage!!!
-- this is freed only by shutdown forever
g: REF GlobalData ← NIL;
modellerIsIdle: PUBLIC BOOL ← FALSE;	-- not TRUE until ModelImpl.Init finishes
ttywindow: Subr.TTYProcs ← NIL;	-- print on this window please
attachEditorRef: ViewerEvents.EventRegistration ← NIL;	-- valid if Tioga and modeller are connected
-- endof MDS Usage!!!

NPREV: NAT = 30;

GlobalData: TYPE = RECORD[
	-- viewers stuff
	ttyTypeScript: TypeScript.TS ← NIL,		-- typescript to print on
	ttyin: IO.Handle ← NIL,				-- IOStream for above
	ttyout: IO.Handle ← NIL,			-- IOStream for above
	msgout: IO.Handle ← NIL,			-- for compiler progress messages
	debugout: IO.Handle ← NIL,			-- for printing debugging messages
	--
	startmodelling: BOOL ← FALSE,
	stopmodelling: BOOL ← TRUE,
	longzone: UNCOUNTED ZONE ← NIL,
	symbolseq: MDModel.SymbolSeq ← NIL,
	working: MDModel.LOCSymbol ← NIL,
	prevstring: ARRAY[0 .. NPREV) OF Rope.Text ← ALL[NIL],
	nprevstring: CARDINAL ← 0
	];

-- local variables
maxdirsize: CARDINAL = 700;	-- the max number of files on the disk


-- modeller algorithm:
--	(MakeModel non-interactive)
-- 1) - 4) StartModelling (see below)
-- 5) determine what needs to be compiled
-- 	if source ~= bcd, the recompile
--	if bcd is ok, but a parameter has changed, then recompile
-- 	record name of bcd produced
-- 	(add internal modulename from bcd files)
-- 6) if notify and there were new sources, generate new model

-- thus non-interactive modelling looks like
-- 	StartModelling X
--	run recompilation analysis algorithm
-- 	MakeModel
--	StopModelling

-- StartModelling
-- 	1) parse model  (try to handle *: id, id:*, 
--  		don't handle omitted parameters and :@loc)
-- 	2) determine we have correct versions
-- 		retrieve them from remote servers
-- 		(now sources are correct)
-- 	3) fill in defaults for initial model
-- 	(now model is fully qualified)
-- 	4) check parameters to source files for accuracy
-- 	(at this point the model is complete and correct)
-- 	(add internal modulename from source files)
--
-- action.filename is the name of a model file
InternalStartModelling: INTERNAL PROC[action: REF MDMain.Transaction] = {
fn: STRING ← [100];
time: LONG CARDINAL;
-- p: PROCESS;
nhits, nmisses: LONG CARDINAL;

{
ttywindow ← action.ttyprintwindow;
time ← Time.Current[];
Subr.errorflg ← FALSE;
(action.ttyout).PutF["StartModelling %s\n", IO.rope[action.filename]];
IF g.startmodelling OR NOT g.stopmodelling THEN {
	CWF.WF0["Error - you have not StoppedModelling.\n"L];
	CWF.WF0["Type y to proceed and I will do a StopModelling for you,\n"L];
	IF ttywindow.Confirm[
		ttywindow.in, ttywindow.out, ttywindow.data,
		"type n or CR and I will quit:  ", 'n] = 'n
	    THEN RETURN;
	InternalStopModelling[];
	IF g = NIL THEN InitializeData[];
	(action.ttyout).PutF["StartModelling %s\n", IO.rope[action.filename]];
	};
-- initialize
g.ttyTypeScript ← action.ttyTypeScript;
g.ttyin ← action.ttyin;
g.ttyout ← action.ttyout;
g.msgout ← action.msgout;
g.debugout ← action.debugout;
g.startmodelling ← TRUE;
g.stopmodelling ← FALSE;
MDModel.numberofbcdsmapped ← MDModel.numberofsourcesparsed ← 0;
Subr.numberofleaders ← 0;
DBStash.SetNLeaders[];
DBStash.SetNHits[];
MDModel.traversetreecalled ← 0;
g.symbolseq ← MDModel.AllocateSymbolSeq[maxsym];
MDUtil.SupportInit[g.symbolseq, g.ttyTypeScript, g.ttyout];
MDModel.StartMDSupport[];
Chk[];
MDModel.ParseInit[g.symbolseq, action.noninteractive, g.ttyTypeScript, ttywindow];
CWF.SWF1[fn, "@%s"L, LOOPHOLE[action.filename]];	-- ProcessFilename likes an @
g.symbolseq.toploc ← MDModel.ProcessFilename[fn];
Chk[];
-- now parse the model
MDModel.ModelParse[g.symbolseq, g.ttyTypeScript, ttywindow];
IF g.symbolseq.toploc.nestedmodel = NIL THEN GOTO PErr;
g.working ← g.symbolseq.toploc;
Chk[];
-- fill up the disk cache
-- this procedure will parse the source files
-- and will move undefineds to be parameters, recursively postorder
-- can't fork here since we're in the monitor
MDDB.BringOverFilesAndCheckAllParms[g.symbolseq, action.noninteractive,
	g.ttyTypeScript, action.ttyprintwindow];
IF Subr.debugflg THEN
	MDModel.ValidateModel[g.symbolseq];
STPSubr.StopSTP[];
CWF.FWF1[DebugWP, "Symbolseq.size %u, "L, @g.symbolseq.size];
PrintElapsedTime[time];
[nhits, nmisses] ← DBStash.GetNHits[];
CWF.FWF2[DebugWP, "DB %lu hits, %lu misses.\n"L, @nhits, @nmisses];
CWF.FWF2[DebugWP, "# bcds mapped in %u, # sources parsed %u.\n"L,
   @MDModel.numberofbcdsmapped, @MDModel.numberofsourcesparsed];
TemporaryStop[];
EXITS
PErr => {
	CWF.WF0["Parsing error - can't continue.\n"L];
	InternalStopModelling[];
	};
};
};

InternalCompile: INTERNAL PROC[uniquename, tryreplacement: BOOL, 
	confirm: REF BOOL] RETURNS[compProblems: BOOL] = {
time: LONG CARDINAL ← Time.Current[];
DBStash.SetNLeaders[];
Subr.numberofleaders ← 0;
MDModel.traversetreecalled ← 0;
CWF.WF0["Compile\n"L];
Chk[];
-- this generates a new model(s)
MoveFiles.InternalPermanentOrTemporary[symbolseq: g.symbolseq,
	working: g.working, temporary: TRUE, force: FALSE, window: ttywindow,
	typeScript: g.ttyTypeScript, ttyout: g.ttyout];
Chk[];
MDModel.numberofbcdsmapped ← MDModel.numberofsourcesparsed ← 0;
-- this procedure may look at bcd's
-- now determine what needs to be compiled and emit comp stmts
-- and bind stmts
[, compProblems] ← MDComp.DetermineRecomp[g.working, g.symbolseq,
	 ttywindow, uniquename, tryreplacement, confirm, g.ttyTypeScript, g.ttyin,
	 g.ttyout, g.msgout];
g.msgout.Flush[];
CWF.FWF2[DebugWP, "# bcds mapped in %u, # sources parsed %u.\n"L,
   @MDModel.numberofbcdsmapped, @MDModel.numberofsourcesparsed];
PrintElapsedTime[time];
Chk[];
TemporaryStop[];
};

InternalStopModelling: INTERNAL PROC = {
CWF.WF0["StopModelling\n"L];
IF g = NIL OR NOT g.startmodelling OR g.stopmodelling THEN {
	CWF.WF0["Error - You must give a StartModelling command first.\n"L];
	RETURN;
	};
IF g.symbolseq.toploc.nestedmodel ~= NIL 
AND g.symbolseq.toploc.nestedmodel.modelchanged THEN {
	IF ttywindow.Confirm[
		ttywindow.in, ttywindow.out, ttywindow.data,
		"You did not save the new model(s).\nAre you sure you want to StopModelling ",
		'n] = 'n
	    THEN {
		CWF.WF0["No.\n"L];
		RETURN;
		}
	ELSE CWF.WF0["Yes.\n"L];
	};
CleanupData[];
};

-- for NoticeAll
--	look at all files on local disk, if there are any
-- 	newer versions, do a Notice on them
NoticeAll: PUBLIC ENTRY PROC = {
nnotice: CARDINAL;
time: LONG CARDINAL;

{
ENABLE {
	ABORTED, Subr.AbortMyself => {
		CWF.WF0["NoticeAll aborted."L];
		GOTO out;
		};
	};

	CheckSource: INTERNAL PROC[spl: MDModel.Symbol, 
		spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL←TRUE] = {
	fi: Dir.FileInfo;
	sploc: MDModel.LOCSymbol;
	IF spl.stype ~= typeLOC THEN RETURN;
	Chk[];
	sploc ← MDModel.NarrowToLOC[spl];
	fi ← MDModel.GetFileInfo[sploc];
	-- this is to allow two bcds with different dates to
	-- be expressed in the model (e.g. Mopcodes)
	IF fi.isBcd THEN RETURN;
	[] ← Dir.NewVersion[fi: fi, src: TRUE ! Directory.Error => GOTO out];
	IF sploc.createtime ~= MDModel.GetSrcCreate[fi] THEN {
		IF InternalNotice[ConvertUnsafe.ToRope[fi.srcFileName]] THEN nnotice ← nnotice+1;
		};
	RETURN;
	EXITS
	out => RETURN;
	};

PrintSeparatorLine[];
time ← Time.Current[];
modellerIsIdle ← FALSE;
CheckStarted[];
Chk[];
Subr.numberofleaders ← 0;
DBStash.SetNLeaders[];
CWF.WF0["NoticeAll begun ....\n"L];
nnotice ← 0;
(g.symbolseq.toploc).TraverseTree[g.symbolseq, CheckSource];
CWF.WF1["%u files noticed.\n"L, @nnotice];
TemporaryStop[];
GOTO out;
EXITS
out => {
	modellerIsIdle ← TRUE;
	PrintElapsedTime[time];
	PrintSeparatorLine[];
	};
};
};

-- for each Notice:
-- 	3) fill in defaults for module being notified
--	(this involves looking at the sourcefile)
-- 	4) check parameters to notified source file for accuracy
--	(this involves looking at the bcd)
InternalNotice: INTERNAL PROC[filename: Rope.Text] 
	RETURNS[noticed: BOOL] = {
main: STRING ← [100];
ext: STRING ← [100];
noticetime: LONG CARDINAL;
fi: Dir.FileInfo;
cap: File.Capability;

	-- done postorder
	CheckSource: PROC[sptop: MDModel.Symbol, spmodel: MDModel.MODELSymbol] 
		RETURNS[proceed: BOOL ← TRUE] = {
	
		-- calls itself recursively
		-- spmodel, noticed, sptop is passed in
		ProcLoc: PROC[spl: MDModel.Symbol] = {
		IF spl = NIL THEN RETURN;
		IF spl.stype = typeLIST THEN 
			MDModel.TraverseList[MDModel.NarrowToLIST[spl], ProcLoc]
		ELSE IF spl.stype = typeLOC THEN {
			sploc: MDModel.LOCSymbol;
			sploc ← MDModel.NarrowToLOC[spl];
			IF LongString.EquivalentString[main, sploc.tail]
			AND LongString.EquivalentString[ext, sploc.sext] 
			AND noticetime ~= sploc.createtime THEN {
				spmodel.modelchanged ← TRUE;
				sploc.createtime ← noticetime;
				fi ← MDModel.GetFileInfo[sploc];
				IF NOT fi.isBcd THEN {
					fi.srcCap ← cap;
					fi.srcCreate ← noticetime;
					fi.srcPresent ← TRUE;
					fi.srcDepSeq ← NIL;
					noticed ← TRUE;
					[] ← MDDB.CheckAndFillInParameters[sptop, sploc, 
						g.symbolseq, spmodel, FALSE, g.ttyTypeScript, NIL];
					-- last parameter shouldn't be NIL        ↑
					IF Subr.debugflg THEN
						g.ttyout.PutF["Notice %s in %s.\n", IO.rope[filename], 
							IO.string[spmodel.modelfilename]];
					-- this adds a new bcd if there happens
					-- to be one lying around
					[] ← Dir.NewVersion[fi: fi, src: FALSE
						! Directory.Error => CONTINUE];
					}
				ELSE {	-- .Bcd in model
				 	[] ← Dir.NewVersion[fi: fi, src: FALSE
						! Directory.Error => CONTINUE];
					};
				};
			};
		};
		
	WITH spt: sptop SELECT FROM
	typeTYPE => ProcLoc[spt.typeval];
	typeAPPL => ProcLoc[spt.applval];
	typeLET => ProcLoc[spt.letval];
	typeMODEL => IF spt.modelchanged THEN {
			spmodel.modelchanged ← TRUE;
			IF Subr.debugflg THEN
				CWF.WF2["%s forces %s model changed.\n"L, 
					spt.modelfilename, spmodel.modelfilename];
			};
	ENDCASE => NULL;
	RETURN[TRUE];
	};


noticed ← FALSE;
g.ttyout.PutF["Notice %s ... ", IO.rope[filename]];
Subr.strcpy[main, LOOPHOLE[filename]];
Process.Yield[];
-- now we can't rely on the disk cache; since the new version 
-- will not be reflected in the cache
cap ← Directory.Lookup[fileName: main, permissions: Directory.ignore
	! Directory.Error => {
		g.ttyout.PutF["Notice Error - %s not on local disk.\n", IO.rope[filename]];
		GOTO out;
		};
	];
noticetime ← Subr.GetCreateDate[cap];
Subr.strcpy[ext, "Mesa"L];
FOR i: CARDINAL DECREASING IN [0 .. main.length) DO
	IF main[i] = '. THEN {
		Subr.SubStrCopy[ext, LOOPHOLE[filename], i + 1];
		main.length ← i;
		EXIT;
		};
	ENDLOOP;
Chk[];
-- postorder is important here
MDModel.TraverseTree[g.symbolseq.toploc.nestedmodel, g.symbolseq, 
	CheckSource, FALSE];
CWF.WF0[IF noticed THEN "noticed.\n"L ELSE "not noticed.\n"L];
TemporaryStop[];
EXITS
out => NULL;
};

SetWorkingModel: PUBLIC ENTRY PROC[modelname: Rope.Text] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["SetWorkingModel Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["SetWorkingModel Aborted.\n"L];
		GOTO out;
		};
	};
CheckStarted[];
modellerIsIdle ← FALSE;
IF modelname = NIL THEN 
	g.working ← g.symbolseq.toploc
ELSE 
	g.working ← LocForModelName[g.symbolseq, LOOPHOLE[modelname]];
IF g.working = NIL THEN 
	CWF.WF1["Error - Could not set working model to %s.\n"L, LOOPHOLE[modelname]]
ELSE IF modelname ~= NIL THEN 
	CWF.WF1["Working Model set to %s.\n"L, LOOPHOLE[modelname]]
ELSE 
	CWF.WF0["Working model reset to outermost model.\n"L];
TemporaryStop[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	PrintSeparatorLine[];
	modellerIsIdle ← TRUE;
	};
}};

LocForModelName: PROC[symbolseq: MDModel.SymbolSeq, modelname: LONG STRING] 
	RETURNS[sploc: MDModel.LOCSymbol] = {

	LookFor: PROC[sp: MDModel.Symbol, sp3: MDModel.MODELSymbol]
		RETURNS[proceed: BOOL ← TRUE] = {
	spl: MDModel.LOCSymbol;
	IF sploc ~= NIL OR sp.stype ~= typeLOC THEN RETURN;
	spl ← MDModel.NarrowToLOC[sp];
	IF spl.nestedmodel ~= NIL AND
	LongString.EquivalentString[spl.nestedmodel.modelfilename, modelname] THEN {
		sploc ← spl;
		RETURN[FALSE];
		};
	};
		
sploc ← NIL;
MDModel.TraverseTree[symbolseq.toploc, symbolseq, LookFor];
};
	
PrintElapsedTime: PROC[oldtime: LONG CARDINAL] = {
elapt: LONG CARDINAL ← Time.Current[];
nleaders: CARDINAL;
nleaders ← Subr.numberofleaders + DBStash.GetNLeaders[];
elapt ← elapt - oldtime;
IF elapt = 0 THEN elapt ← 1;
CWF.FWF3[DebugWP, "Elapsed seconds: %lu, leaders: %u, traversetrees: %u\n"L, 
	@elapt, @nleaders, @MDModel.traversetreecalled];
Subr.numberofleaders ← 0;
MDModel.traversetreecalled ← 0;
DBStash.SetNLeaders[];
};

-- these are the procedures that are exported but in fact 
-- just call INTERNAL procedures

StartModelling: PUBLIC ENTRY PROC[action: REF MDMain.Transaction] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED, Subr.AbortMyself => {
		CWF.WF0["StartModelling Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	};
PrintSeparatorLine[];
modellerIsIdle ← FALSE;
IF g = NIL THEN InitializeData[];	-- will not delete any string heaps
InternalStartModelling[action];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	PrintSeparatorLine[];
	modellerIsIdle ← TRUE;
	};
}};

ReStartModelling: PUBLIC ENTRY PROC[action: REF MDMain.Transaction] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED, Subr.AbortMyself => {
		CWF.WF0["ReStartModelling Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	};
PrintSeparatorLine[];
modellerIsIdle ← FALSE;
IF g ~= NIL AND g.startmodelling THEN 
	InternalStopModelling[];
IF g = NIL THEN InitializeData[];
InternalStartModelling[action];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	PrintSeparatorLine[];
	modellerIsIdle ← TRUE;
	};
}};

-- for MakeModel:
-- 	6) if necessary, generate config and bind stmt
-- 	7) if were new sources, generate new model
MakeModel: PUBLIC ENTRY PROC[action: REF MDMain.Transaction] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED, Subr.AbortMyself => {
		CWF.WF0["MakeModel Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	};
PrintSeparatorLine[];
g.ttyout.PutF["MakeModel %s\n", IO.rope[action.filename]];
modellerIsIdle ← FALSE;
IF g = NIL THEN InitializeData[];
Chk[];
InternalStartModelling[action];
Chk[];
[] ← InternalCompile[FALSE, FALSE, NIL];
Chk[];
InternalStopModelling[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};

Compile: PUBLIC ENTRY PROC[
   action: REF MDMain.Transaction, 
   uniquename, tryreplacement: BOOL, confirm: REF BOOL] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Compile Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Compile Aborted.\n"L];
		GOTO out;
		};
	};
PrintSeparatorLine[];
modellerIsIdle ← FALSE;
IF g = NIL THEN {	-- StartModelling not given, do it for him
	IF g = NIL THEN InitializeData[];
	InternalStartModelling[action];
	};
CheckStarted[];
[] ← InternalCompile[uniquename, tryreplacement, confirm];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};

-- for Temporary:
-- 	just write out the model from memory
Temporary: PUBLIC ENTRY PROC = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED =>  {
		CWF.WF0["Temporary Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Temporary Aborted.\n"L];
		GOTO out;
		};
	};
PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
MoveFiles.InternalPermanentOrTemporary[symbolseq: g.symbolseq,
	working: g.working, temporary: TRUE, force: TRUE, window: ttywindow,
	typeScript: g.ttyTypeScript, ttyout: g.ttyout];
TemporaryStop[];
GOTO out;
EXITS
out => {
	modellerIsIdle ← TRUE;
	IF clean THEN CleanupData[];
	PrintSeparatorLine[];
	};
}};

-- for Permanent:
-- 	store back any files on which a Notify has been done
Permanent: PUBLIC ENTRY PROC = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED =>  {
		CWF.WF0["Permanent Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Permanent Aborted.\n"L];
		GOTO out;
		};
	};
PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
MoveFiles.InternalPermanentOrTemporary[symbolseq: g.symbolseq,
	working: g.working, temporary: FALSE, force: FALSE, window: ttywindow,
	typeScript: g.ttyTypeScript, ttyout: g.ttyout];
TemporaryStop[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};


-- for StopModelling
-- 	query if Permanent or Temporary hasn't been done
--	shut down stuff, free memory
StopModelling: PUBLIC ENTRY PROC = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED, Subr.AbortMyself => {
		CWF.WF0["StopModelling Aborted.\n"L];
		clean ← TRUE;
				GOTO out;
		};
	};
PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
InternalStopModelling[];
GOTO out;
EXITS
out => {
	modellerIsIdle ← TRUE;
	IF clean THEN CleanupData[];
	PrintSeparatorLine[];
	};
}};

-- utility to print out model
-- uses the working model
-- will FreeString[modelname];
Type: PUBLIC ENTRY PROC[modelname: Rope.Text, default: BOOL] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Type Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Type Aborted.\n"L];
		GOTO out;
		};
	};
root: MDModel.LOCSymbol;
PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
root ← g.working;
IF modelname ~= NIL THEN {
	root ← LocForModelName[g.symbolseq, LOOPHOLE[modelname]];
	CWF.WF1["Type %s\n"L, LOOPHOLE[modelname]];
	IF root = NIL THEN {
		CWF.WF1["Error - can't find %s.\n"L, LOOPHOLE[modelname]];
		GOTO out;
		};
	}
ELSE CWF.WF0["Type.\n"L];
MDUtil.PrintNewModelStream[g.symbolseq, root.nestedmodel, NIL, 
	NIL, NOT default, g.ttyTypeScript, g.ttyout];
TemporaryStop[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};

-- this is running in the background when called so
-- it catches an unusual number of signals
Notice: PUBLIC ENTRY PROC[filename: Rope.Text] = {
{
ENABLE {
	ABORTED, Subr.AbortMyself => {
		CWF.WF0["Notice aborted."L];
		GOTO out;
		};
	};


PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
[] ← InternalNotice[filename];
TemporaryStop[];
GOTO out;
EXITS
out => {
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
};
};

Bind: PUBLIC ENTRY PROC = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Bind Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Bind Aborted.\n"L];
		GOTO out;
		};
	};
spmodel: MDModel.MODELSymbol;
PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
spmodel ← g.working.nestedmodel;
[] ← MDComp.NewBind[spmodel, g.symbolseq, TRUE, TRUE, NIL,
	spmodel.modelfilename, spmodel.modelcreate, 
	ttywindow, g.ttyTypeScript, g.ttyin, g.ttyout, g.msgout];
TemporaryStop[];
GOTO out;
EXITS
out => {
	modellerIsIdle ← TRUE;
	IF clean THEN CleanupData[];
	PrintSeparatorLine[];
	};
}};

Loader: PUBLIC ENTRY PROC[tryreplacement: BOOL] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Modeller Loader Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Modeller Loader Aborted.\n"L];
		GOTO out;
		};
	};
spmodel: MDModel.MODELSymbol;
PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
spmodel ← g.working.nestedmodel;
MDLoad.LoadBcdsAndResolveImports[spmodel, g.symbolseq, tryreplacement, ttywindow, g.ttyout];
TemporaryStop[];
GOTO out;
EXITS
out => {
	modellerIsIdle ← TRUE;
	IF clean THEN CleanupData[];
	PrintSeparatorLine[];
	};
}};

UnLoader: PUBLIC ENTRY PROC = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Modeller UnLoader Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Modeller UnLoader Aborted.\n"L];
		GOTO out;
		};
	};
spmodel: MDModel.MODELSymbol;
PrintSeparatorLine[];
CheckStarted[];
modellerIsIdle ← FALSE;
spmodel ← g.working.nestedmodel;
CWF.WF0["Unloading modules.\n"L];
-- this will actually delete all the modules code segments, etc
MDLoad.UnLoad[spmodel, g.symbolseq, TRUE];
TemporaryStop[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};

Begin: PUBLIC ENTRY PROC[action: REF MDMain.Transaction, confirm: REF BOOL] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Begin Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Begin Aborted.\n"L];
		GOTO out;
		};
	};
spmodel: MDModel.MODELSymbol;
compProblems: BOOL;
modellerIsIdle ← FALSE;
PrintSeparatorLine[];
IF g = NIL THEN {	-- StartModelling not given, do it for him
	IF g = NIL THEN InitializeData[];
	InternalStartModelling[action];
	};
CheckStarted[];
spmodel ← g.working.nestedmodel;
-- this will actually delete all the modules code segments, etc
MDLoad.UnLoad[spmodel, g.symbolseq, TRUE];
compProblems ← InternalCompile[FALSE, FALSE, confirm];
IF compProblems THEN 
	CWF.WF0["Loading and starting aborted.\n"L]
ELSE {
	MDLoad.LoadBcdsAndResolveImports[spmodel, g.symbolseq, FALSE, ttywindow, g.ttyout];
	MDLoad.StartAllControlBcds[spmodel, g.symbolseq];
	spmodel.started ← TRUE;
	};
-- modellerIsIdle will be reset in MDLoadImpl
TemporaryStop[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};

Continue: PUBLIC ENTRY PROC[confirm: REF BOOL] = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Continue Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Continue Aborted.\n"L];
		GOTO out;
		};
	};
compProblems: BOOL;
spmodel: MDModel.MODELSymbol;
modellerIsIdle ← FALSE;
PrintSeparatorLine[];
CheckStarted[];
spmodel ← g.working.nestedmodel;
compProblems ← InternalCompile[FALSE, FALSE, confirm];
IF compProblems THEN 
	CWF.WF0["Loading with replacement aborted.\n"L]
ELSE {
	MDLoad.LoadBcdsAndResolveImports[spmodel, g.symbolseq, FALSE, ttywindow, g.ttyout];
	IF NOT spmodel.started THEN 
		MDLoad.StartAllControlBcds[spmodel, g.symbolseq];
	spmodel.started ← TRUE;
	};
-- modellerIsIdle will be reset in MDLoadImpl
TemporaryStop[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};

Start: PUBLIC ENTRY PROC = {
clean: BOOL ← FALSE;
{
ENABLE {
	ABORTED => {
		CWF.WF0["Start Aborted.\n"L];
		clean ← TRUE;
		GOTO out;
		};
	Subr.AbortMyself => {
		CWF.WF0["Start Aborted.\n"L];
		GOTO out;
		};
	};
spmodel: MDModel.MODELSymbol;
modellerIsIdle ← FALSE;
PrintSeparatorLine[];
CheckStarted[];
spmodel ← g.working.nestedmodel;
MDLoad.StartAllControlBcds[spmodel, g.symbolseq];
-- modellerIsIdle will be reset in MDLoadImpl
TemporaryStop[];
GOTO out;
EXITS
out => {
	IF clean THEN CleanupData[];
	modellerIsIdle ← TRUE;
	PrintSeparatorLine[];
	};
}};

-- this is called to initialize the world
InitializeData: PROC = {
-- this storage is freed by CleanupData[];
longzone: UNCOUNTED ZONE;
IF g ~= NIL THEN ERROR;
-- Subr.LongZone will call SubrInit with 256 pages if needed
longzone ← Subr.LongZone[];
g ← NEW[GlobalData ← []];
g.longzone ← longzone;
};

-- this frees all of the data structures, until InitializeData is called again
CleanupData: INTERNAL PROC = {
npages: CARDINAL;
IF g = NIL THEN RETURN;
IF g.working ~= NIL THEN 
	-- doesn't delete the code segments
	MDLoad.UnLoad[g.working.nestedmodel, g.symbolseq, FALSE];
MDModel.StopMDSupport[];
STPSubr.StopSTP[];
MDModel.FreeSymbolSeq[@g.symbolseq];	
MDModel.StopScanner[];
npages ← DBStash.ForceOut[];
CWF.FWF1[DebugWP, "%u pages used in DB huge space.\n"L, @npages];
TemporaryStop[];
FREE[@g];
Subr.SubrStop[];		-- frees the long zone
modellerIsIdle ← TRUE;
};

TemporaryStop: PROC = 
	{
	g.ttyout.Flush[];
	IF g.debugout ~= NIL THEN g.debugout.Flush[];
	};
	
-- only prints if the debugging window is available
DebugWP: PUBLIC PROC[ch: CHAR] = {
IF g ~= NIL AND g.debugout ~= NIL THEN
	g.debugout.PutChar[ch];
};

Chk: PROC = {
IF TypeScript.UserAbort[g.ttyTypeScript] THEN SIGNAL Subr.AbortMyself;
};

-- this will do any delayed notices
CheckStarted: INTERNAL PROC = {
IF g = NIL THEN {
	CWF.WF0["Error - StartModelling has not been given.\n"L];
	SIGNAL Subr.AbortMyself;
	};
IF g.startmodelling AND g.nprevstring > 0 THEN {
	FOR i: CARDINAL IN [0 .. g.nprevstring) DO
		[] ← InternalNotice[g.prevstring[i]];
		g.prevstring[i] ← NIL;
		ENDLOOP;
	g.nprevstring ← 0;
	};
};
	
PrintSeparatorLine: PUBLIC PROC = {
CWF.FWF0[DebugWP, "-----------------\n"L];
CWF.WF0["-----------------\n"L];
};

-- code from Tioga


AttachSymbiote: PUBLIC PROC[msgout: IO.Handle] =
	{
	IF attachEditorRef = NIL THEN 
	   attachEditorRef ← ViewerEvents.RegisterEventProc[
	       proc: CallProcedureForNotice, event: $save, before: FALSE];
	MDUtil.AcquireMsgLock[];
	msgout.Put[IO.string["Editor set to call modeller.\n"L]
		! UNWIND => MDUtil.ReleaseMsgLock[]];
	MDUtil.ReleaseMsgLock[];
	};

DetachSymbiote: PUBLIC PROC[msgout: IO.Handle] = {
	IF attachEditorRef ~= NIL THEN
		ViewerEvents.UnRegisterEventProc[attachEditorRef, save];
	attachEditorRef ← NIL;
	MDUtil.AcquireMsgLock[];
	msgout.Put[IO.string["Editor detached from modeller.\n"L]
		! UNWIND => MDUtil.ReleaseMsgLock[]];
	MDUtil.ReleaseMsgLock[];
	};

-- this is the procedure called by the editor
-- can't print anything in this procedure
CallProcedureForNotice: ViewerEvents.EventProc = TRUSTED { 
   ENABLE ANY => GOTO out;
   flat: Rope.Text;
   IF g = NIL OR NOT g.startmodelling THEN RETURN;
   IF viewer.file = NIL THEN RETURN;
   flat ← RopeInline.InlineFlatten[viewer.file];
   IF NOT modellerIsIdle THEN {
		IF g.nprevstring >= g.prevstring.LENGTH THEN RETURN;
		g.prevstring[g.nprevstring] ← flat;
		g.nprevstring ← g.nprevstring + 1;
		RETURN;
		};
	-- this will acquire the monitor lock
	Process.Detach[FORK Notice[flat]];
	EXITS
   out => NULL;
   };
   
PrintFileInfo: UserExec.CommandProc = TRUSTED {
ENABLE Subr.AbortMyself => {
	CWF.WF0["XFIPrint aborted.\n"L];
	GOTO out;
	};
		
	PrintLoc: PROC[sp: MDModel.Symbol, spmodel: MDModel.MODELSymbol]
		RETURNS[proceed: BOOL ← TRUE] = {
	sploc: MDModel.LOCSymbol;
	fi: Dir.FileInfo;
	IF sp.stype ~= typeLOC THEN RETURN;
	sploc ← MDModel.NarrowToLOC[sp];
	fi ← sploc.fi;
	CWF.WF4["%s: (create %w, vers %v%s) "L,
		fi.bcdFileName, @fi.bcdCreate, @fi.bcdVers, 
		IF fi.bcdPresent THEN ", bcdPresent"L ELSE ""L];
	CWF.WF4["mod %s\n\t%s: (create %w%s)\n"L,
		fi.moduleName, fi.srcFileName, @fi.srcCreate, 
		IF fi.srcPresent THEN ", srcPresent"L ELSE ""L];
	IF UserExec.UserAbort[exec] THEN SIGNAL Subr.AbortMyself;
	};
	
MDModel.TraverseTree[g.symbolseq.toploc, g.symbolseq, PrintLoc];
EXITS
out => NULL;
};

CWFWRoutine: PROC[uns: LONG POINTER, form: LONG STRING, wp: PROC[CHAR]] = {
time: LONG CARDINAL ← LOOPHOLE[uns, LONG POINTER TO LONG CARDINAL]↑;
IF time = 0 THEN CWF.FWF0[wp, "(Null)"L] ELSE CWF.FWF1[wp, "%lt"L, uns];
};

Init: PROC = {
UserExec.RegisterCommand["XFIPrint.~", PrintFileInfo];
CWF.SetCode['w, CWFWRoutine];
};

Init[];
}.