-- LoaderSupportImpl.mesa
-- last edit by Schmidt, January 6, 1983 1:59 pm
-- last edit by Satterthwaite, February 9, 1983 12:55 pm

DIRECTORY
  BcdDefs: TYPE USING [Base, EXPIndex, FTIndex, FTNull, FTSelf,
   GFTIndex, Link, MTIndex, NameRecord, NullVersion, NullLink],
  BcdOps: TYPE USING [BcdBase, EXPHandle, FTHandle, MTHandle, NameString,
    ProcessExports, ProcessModules],
  CompilerOps: TYPE USING [Start],
  CWF: TYPE USING [FWF0, FWF1, WF0, WF1, WF2, WF4],
  Directory: TYPE USING [Handle, Lookup],
  File: TYPE USING [Capability, PageCount],
  IO: TYPE USING[GetChar, Handle, PutChar, PutF, PutRope, Signal],
  LowLoader: TYPE USING [AllocateInterfaceSeq, ConvertLink, CopyNStoLS, EqualStringAndName,
  		FindVariableLink, FreeInterfaceSeq, InterfaceSeq],
  LongString: TYPE USING [EqualString, EquivalentString],
  PilotLoadStateFormat: TYPE USING [ConfigIndex],
  PilotLoadStateOps: TYPE USING [AcquireBcd, ConfigIndex, InputLoadState, MapConfigToReal, 
  	ReleaseLoadState],
  PrincOps: TYPE USING [ControlLink, GFTIndex, GFTNull,
   GlobalFrameHandle, NullLink, UnboundLink],
  PrincOpsRuntime: TYPE USING [GetFrame, GFT],
  Rope: TYPE USING[Lower, ROPE, Text],
  RopeInline: TYPE USING[InlineFlatten],
  Runtime: TYPE USING [IsBound, RunConfig],
  Subr: TYPE USING [AbortMyself, LongZone, MakeTTYProcs, TTYProcs, SubrStop],
  System: TYPE USING [GetClockPulses, PulsesToMicroseconds],
  TimeStamp: TYPE USING [Null, Stamp],
  UECP: TYPE USING[Argv, Parse],
  UserExec: TYPE USING[CommandProc, GetStreams, RegisterCommand, UserAbort];

LoaderSupportImpl: MONITOR 
IMPORTS BcdOps, CompilerOps, CWF, Directory, IO, LowLoader, LongString, PilotLoadStateOps, 
	PrincOpsRuntime, RopeInline, Runtime, Subr, System, UECP, UserExec
EXPORTS LowLoader
SHARES File = {

NEXPRECORDS: CARDINAL = 1500;	-- # of export records in load state
LoadStateSeqRecord:  TYPE = RECORD[
	nbcds: CARDINAL ← 0,	-- nBcds in loadstate when last examined
	size: CARDINAL ← 0,
	body: SEQUENCE maxsize: CARDINAL OF LowLoader.InterfaceSeq
	];

-- MDS Usage!
loadstateseq: LONG POINTER TO LoadStateSeqRecord ← NIL;
-- end of MDS usage
-- procedures having to do with the load state

-- must call IncorporateLoadStateChanges before calling this
GetIntFromLoadState: PUBLIC PROC[intname: LONG STRING, vers: TimeStamp.Stamp] 
	RETURNS[interfaceseq: LowLoader.InterfaceSeq] = {
IF loadstateseq = NIL THEN ERROR;
FOR i: CARDINAL DECREASING IN [0.. loadstateseq.size) DO
	interfaceseq ← loadstateseq[i];
	IF (vers = TimeStamp.Null OR interfaceseq.versstamp = vers) 
	AND LongString.EqualString[interfaceseq.intname, intname] THEN
		RETURN[interfaceseq];
	ENDLOOP;
RETURN[NIL];
};

AddToLoadState: PUBLIC PROC[interfaceseq: LowLoader.InterfaceSeq] = {
IF loadstateseq = NIL THEN ERROR;
IF loadstateseq.size >= loadstateseq.maxsize THEN ERROR;
loadstateseq[loadstateseq.size] ← interfaceseq;
loadstateseq.size ← loadstateseq.size + 1;
interfaceseq.isfromloadstate ← TRUE;
};

IncorporateLoadStateChanges: PUBLIC PROC[window: Subr.TTYProcs, askCompiler: BOOL] = {
ENABLE UNWIND => PilotLoadStateOps.ReleaseLoadState[];
nbcds: CARDINAL;
bcdbase: BcdOps.BcdBase;
namestring: BcdOps.NameString;
longzone: UNCOUNTED ZONE ← Subr.LongZone[];
config: CARDINAL;
p: LONG CARDINAL;

	ForEachExport: PROC[eth: BcdOps.EXPHandle, eti: BcdDefs.EXPIndex]
		RETURNS[stop: BOOL] = {
	fth: BcdOps.FTHandle;
	blink: BcdDefs.Link;
	rgfi: CARDINAL;
	newclink: PrincOps.ControlLink;
	interfaceseq: LowLoader.InterfaceSeq;
	stop ← FALSE;
	fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][eth.file];
	interfaceseq ← ProcessInterface[namestring, eth.name, fth.version,
		bcdbase, eth.size];
	IF eth.size > interfaceseq.maxsize THEN {
		PilotLoadStateOps.ReleaseLoadState[];
		ERROR;
		};
	-- now fill in the exports
	FOR i: CARDINAL IN [0 .. eth.size) DO
		blink ← eth.links[i];
		rgfi ← PilotLoadStateOps.MapConfigToReal[blink.gfi, config];
		IF rgfi = PrincOps.GFTNull THEN LOOP;
		SELECT blink.vtag FROM
		var => 	{
			frame: PrincOps.GlobalFrameHandle;
			frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[rgfi]];
			IF frame = NIL THEN
				newclink ← PrincOps.UnboundLink	-- NIL due to Runtime.SelfDestruct[]
			ELSE
				newclink ← LowLoader.FindVariableLink[blink, NIL, frame, bcdbase];
			};
		proc0, proc1=> {
			newclink ← LowLoader.ConvertLink[blink];
			newclink.gfi ← rgfi;
			};
		type => newclink ← PrincOps.NullLink;	-- error
		ENDCASE => ERROR;
		interfaceseq[i] ← [clink: newclink, blink: BcdDefs.NullLink];
		ENDLOOP;
	interfaceseq.size ← eth.size;
	};
	
	-- this fills in the frame pointers
	ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
		RETURNS[stop: BOOL] = {
	fth: BcdOps.FTHandle;
	rgfi: CARDINAL;
	interfaceseq: LowLoader.InterfaceSeq;
	frame: PrincOps.GlobalFrameHandle;
	stop ← FALSE;
	IF mth.file = BcdDefs.FTSelf OR mth.file = BcdDefs.FTNull THEN RETURN;
	fth ← @LOOPHOLE[bcdbase + bcdbase.ftOffset, BcdDefs.Base][mth.file];
	interfaceseq ← ProcessInterface[namestring, mth.name, fth.version,
		bcdbase, 1];
	IF interfaceseq.maxsize = 0 THEN {
		PilotLoadStateOps.ReleaseLoadState[];
		ERROR;
		};
	-- now fill in module pointer
	rgfi ← PilotLoadStateOps.MapConfigToReal[mth.gfi, config];
	frame ← PrincOpsRuntime.GetFrame[PrincOpsRuntime.GFT[rgfi]];
	interfaceseq[0] ← [clink: LOOPHOLE[frame], blink: BcdDefs.NullLink];
	interfaceseq.size ← 1;
	};
	
	wp: PROC[ch: CHAR] = {
	window.out.PutChar[ch];
	};
	
{
oldNBcds: CARDINAL ← 0;
p ← System.PulsesToMicroseconds[System.GetClockPulses[]];
IF loadstateseq = NIL THEN
	loadstateseq ← longzone.NEW[LoadStateSeqRecord[NEXPRECORDS]]
ELSE oldNBcds ← loadstateseq.nbcds;
IF askCompiler AND NOT Runtime.IsBound[CompilerOps.Start] THEN {
	IF window.Confirm[
		window.in, window.out, window.data,
		"Do you want to have Compiler.bcd loaded right now ", 'y] = 'y
	    THEN {
		cap: File.Capability;
		CWF.FWF0[wp, "Yes.\nLoading ... "L];
		{
		ENABLE ANY => { CWF.FWF0[wp, "failed.\n"L]; GOTO out};
		cap ← Directory.Lookup["compiler.bcd"L];
		Runtime.RunConfig[file: cap, offset: 1, codeLinks: TRUE];
		CWF.FWF0[wp, "done.\n"L];
		EXITS
		out => NULL;
		}}
	ELSE CWF.FWF0[wp, "No.\n"L];
	};
nbcds ← PilotLoadStateOps.InputLoadState[];
IF oldNBcds = nbcds THEN {
	PilotLoadStateOps.ReleaseLoadState[];
	RETURN;	-- ok, no changes
	};
CWF.FWF0[wp, "Filling in from Pilot load state... "L];
FOR config IN [oldNBcds .. nbcds) DO
	bcdbase ← PilotLoadStateOps.AcquireBcd[config];
	-- this will be true only if this is a fake config
	-- that I generate
	IF bcdbase.version = BcdDefs.NullVersion THEN LOOP;
	namestring ← LOOPHOLE[bcdbase + bcdbase.ssOffset];
	[] ← BcdOps.ProcessExports[bcdbase, ForEachExport];
	[] ← BcdOps.ProcessModules[bcdbase, ForEachModule];
	ENDLOOP;
PilotLoadStateOps.ReleaseLoadState[];
p ← (System.PulsesToMicroseconds[System.GetClockPulses[]] - p)/1000;
loadstateseq.nbcds ← nbcds;
CWF.FWF1[wp, "done (%lu millisec).\n"L, @p];
}};

ProcessInterface: PROC[namestring: BcdOps.NameString, name: BcdDefs.NameRecord,
	version: TimeStamp.Stamp, bcdbase: BcdOps.BcdBase, size: CARDINAL] 
	RETURNS[interfaceseq: LowLoader.InterfaceSeq] = {
oldinterfaceseq: LowLoader.InterfaceSeq;
interfaceseq ← NIL;
FOR i: CARDINAL IN [0 .. loadstateseq.size) DO
	oldinterfaceseq ← loadstateseq[i];
	IF LowLoader.EqualStringAndName[oldinterfaceseq.intname, namestring, name]
	AND oldinterfaceseq.versstamp = version THEN {
		interfaceseq ← oldinterfaceseq;
		EXIT;
		};
	ENDLOOP;
IF interfaceseq = NIL THEN {
	intname: STRING ← [100];
	LowLoader.CopyNStoLS[intname, bcdbase, name];
	interfaceseq ← LowLoader.AllocateInterfaceSeq[intname, size];
	interfaceseq.isfromloadstate ← TRUE;
	loadstateseq[loadstateseq.size] ← interfaceseq;
	loadstateseq.size ← loadstateseq.size + 1;
	IF loadstateseq.size >= loadstateseq.maxsize THEN {
		PilotLoadStateOps.ReleaseLoadState[];
		ERROR;
		};
	interfaceseq.versstamp ← version;
	};
};

PrintCLink: PROC[j: CARDINAL, link: PrincOps.ControlLink] = {
IF link = PrincOps.NullLink THEN 
	CWF.WF1["#%u: NullLink\n"L, @j]
ELSE IF link = PrincOps.UnboundLink THEN 
	CWF.WF1["#%u: UnboundLink\n"L, @j]
ELSE {
	gfi, ep: CARDINAL;
	gfi ← link.gfi;
	ep ← link.ep;
	CWF.WF4["#%u: gfi %bB ep %u, address %bB\n"L, @j, @gfi, @ep, @link];
	};
};

PrintInt: PROC[interfaceseq: LowLoader.InterfaceSeq] = {
j: CARDINAL;
CWF.WF2["%s of %lt:\n"L, interfaceseq.intname, 
	@interfaceseq.versstamp.time];
FOR j IN [0 .. interfaceseq.size) DO
	PrintCLink[j, interfaceseq[j].clink];
	ENDLOOP;
};

PrintInterfacesFromLoadState: UserExec.CommandProc = TRUSTED {
ENABLE Subr.AbortMyself => {
	CWF.WF0["LSPrint aborted.\n"L];
	GOTO out;
	};
argv: UECP.Argv ← UECP.Parse[event.commandLine];
ttyprocs: Subr.TTYProcs ← Subr.MakeTTYProcs[
	exec.GetStreams[].in, exec.GetStreams[].out, exec, MyConfirm];
IncorporateLoadStateChanges[ttyprocs, FALSE];
CWF.WF1["Pilot Loadstate, total %u interface records.\n"L, @loadstateseq.size];
FOR parm: CARDINAL IN [1 .. argv.argc) DO
 	flat: Rope.Text ← LOOPHOLE[RopeInline.InlineFlatten[argv[parm]]];
 	intname: LONG STRING ← LOOPHOLE[flat];
	IF LongString.EquivalentString[intname, "All"L] THEN {
		-- print them all out
		FOR i: CARDINAL IN [0 .. loadstateseq.size) DO
			IF UserExec.UserAbort[exec] THEN SIGNAL Subr.AbortMyself;
			PrintInt[loadstateseq[i]];
			ENDLOOP;
		}
	ELSE {
		FOR j: CARDINAL IN [0 .. loadstateseq.size) DO
			IF LongString.EquivalentString[intname, loadstateseq[j].intname] THEN {
				PrintInt[loadstateseq[j]];
				EXIT;
				};
			REPEAT
			FINISHED => 
				CWF.WF1["Interface %s not found in load state.\n"L, intname];
			ENDLOOP;
		};
	ENDLOOP;
EXITS
out => NULL;
};

MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR] RETURNS[CHAR] = {
	ch: CHAR;
	bs: IO.Handle;
	out.PutRope[msg];
	DO
		ENABLE IO.Signal => TRUSTED{IF ec = Rubout THEN LOOP};
		out.PutF["? "];
		bs ← IF in.backingStream = NIL THEN in ELSE in.backingStream;
		ch ← bs.GetChar[];
		IF ch = '\n THEN ch ← dch;
		ch ← Rope.Lower[ch];
		RETURN[ch];
		ENDLOOP;
	};
-- call only once
ShutDown: PUBLIC PROC = {
IF loadstateseq ~= NIL THEN 
	FOR i: CARDINAL IN [0 .. loadstateseq.size) DO
		LowLoader.FreeInterfaceSeq[loadstateseq[i]];
		ENDLOOP;
Subr.SubrStop[];
};


OnceInit: PROC = {
UserExec.RegisterCommand["XLSPrint.~", PrintInterfacesFromLoadState];
};		
	

OnceInit[];
}.