RunInternal:
PROC [cmd: Commander.Handle, name:
ROPE, unboundOK: SymTab.Ref, runEvenIfAlreadyRun, runEvenIfUnbound, loadOnly, quote, preferOptimized, printTimings:
BOOL, patchByteSize:
CARD]
RETURNS [error:
BOOL ¬
FALSE] ~
TRUSTED {
given: ROPE ~ PFS.RopeFromPath[PFS.AbsoluteName[PFS.PathFromRope[name]]];
baseStart: INT ~ given.FindBackward["/"]+1;
afterBase: INT ~ given.SkipTo[baseStart, ".!"];
moduleName: ROPE ~ given.Substr[start: baseStart, len: afterBase-baseStart];
hasExt: BOOL ~ given.Find[".", baseStart] >= 0;
startFind: CARD ~ BasicTime.GetClockPulses[];
searchResult: SearchResult ~ FindWithHacks[given, quote, preferOptimized, baseStart, afterBase, Append];
findPulses: CARD ~ BasicTime.GetClockPulses[] - startFind;
torun: PATH ~ searchResult.torun;
unixName: ROPE ~ PFS.PFSNameToUnixName[torun];
install: IL.SymEntry ¬ NIL;
start: IL.SymEntry ¬ NIL;
thisFile: IL.FileEntry ¬ NIL;
thisTime: BasicTime.GMT ¬ BasicTime.nullGMT;
thisSize: CARD ¬ 0;
hasInstall, hasStart: BOOL;
ilPulses, libPulses, commitPulses, installPulses, runPulses: CARD ¬ 0;
dont: BOOL ¬ FALSE;
wasAborted: BOOL ¬ FALSE;
Append:
PROC [err:
BOOL, fmt:
ROPE, v:
LIST OF IO.Value] ~
CHECKED {
Process.CheckForAbort[];
IF err
OR
NOT Basics.IsBound[SimpleFeedback.PutFL]
THEN {
s: IO.STREAM ~ IF err THEN cmd.err ELSE cmd.out;
IO.PutFL[s, fmt, v];
IO.PutChar[s, '\n];
error ¬ TRUE;
}
ELSE {
SimpleFeedback.PutFL[routerName: $RunCommands, msgType: oneLiner, msgClass: $Ran, format: fmt, list: v]
};
IF err THEN error ¬ TRUE;
};
Bitch:
PROC [format:
ROPE] ~
CHECKED {
Append[FALSE, format, LIST[[rope[PFS.RopeFromPath[torun]]], [cardinal[thisSize]], [time[thisTime]]] ]
};
BitchAsErr:
PROC [format:
ROPE] ~
CHECKED {
Append[TRUE, format, LIST[[rope[PFS.RopeFromPath[torun]]], [cardinal[thisSize]], [time[thisTime]]] ]
};
TestErr:
UNSAFE
PROC [ile:
IL.Error, from:
ROPE]
RETURNS [
BOOL] ~ {
IF ile=NIL THEN RETURN [FALSE];
Append[TRUE, "%g => Error[%g, %g, \"%q\"]", LIST[[rope[from]], [integer[ile.fatal]], [integer[ile.code]], [rope[UXStrings.ToRope[ile.msg]]]]];
RETURN [TRUE]};
Seconds:
PROC[p: BasicTime.Pulses]
RETURNS[
REAL] ~
CHECKED
INLINE {
RETURN[BasicTime.PulsesToSeconds[p]];
};
Inner:
PROC =
TRUSTED {
ENABLE UNWIND => [] ¬ TestErr[IL.AbortIncrementalLoad[], "UNWIND"];
PatchSizeCProc:
PROCEDURE []
RETURNS [
IL.CProc] ~
TRUSTED {
PatchSizeProcAddress:
PROCEDURE []
RETURNS [
CARD32] ~
TRUSTED
MACHINE
CODE {
"PatchSizeProcAddress"
};
cProc: IL.CProc ~ [PatchSizeProcAddress[]];
RETURN [cProc];
};
PatchSizeClientData:
PROCEDURE [byteSize:
CARD]
RETURNS [
POINTER] ~
TRUSTED {
clientData: REF CARD ~ NEW[CARD ¬ byteSize];
RETURN [LOOPHOLE[clientData]];
};
uname: UnixString ~ UXStrings.Create[unixName];
install ¬ LookupText["←XR←install←", moduleName];
start ¬ LookupText["←XR←run←", moduleName];
ilPulses ¬ BasicTime.GetClockPulses[];
IF TestErr[
ile:
IL.IncrementalLoadFile[
fName: uname,
patchSizeProc: PatchSizeCProc[],
patchSizeClientData: PatchSizeClientData[byteSize: patchByteSize]],
from: "Load"] THEN GOTO Abort;
thisFile ¬ IL.GetPrevFileEntry[NIL];
thisTime ¬ LOOPHOLE[thisFile.fMTime+unixLag];
thisSize ¬ thisFile.fSize;
IF (
NOT runEvenIfAlreadyRun)
AND (install#
NIL
AND install.fe#
NIL
AND install.fe.fMTime=thisFile.fMTime
AND install.fe.fSize=thisFile.fSize
OR start#
NIL
AND start.fe#
NIL
AND start.fe.fMTime=thisFile.fMTime
AND start.fe.fSize=thisFile.fSize)
THEN {
BitchAsErr["%g (s=%g m=%g) already loaded"];
dont ¬ TRUE; GOTO Abort};
BEGIN
tmpPulses: CARD;
ilPulses ¬ (libPulses ¬ commitPulses ¬ BasicTime.GetClockPulses[]) - ilPulses;
WHILE
NOT dont
AND
IL.CommitIncrementalLoad[] #
NIL
DO
missing: LIST OF ROPE ¬ NIL;
Consumer:
IL.UndefinedSymbolProc ~
TRUSTED {
missing ¬ CONS[UXStrings.ToRope[se.name], missing];
RETURN [TRUE]};
consumer: POINTER TO ProcRep ~ LOOPHOLE[Consumer];
IL.EnumerateUndefinedSymbols[consumer.pcStart, consumer];
IF missing=NIL THEN EXIT;
WHILE missing#
NIL
DO
missingU: UnixString ~ UXStrings.Create[missing.first];
found: LibSearch.Bool;
libfd: LibSearch.FD;
liboffset, magic: INT;
modulenameU: UnixString;
moduleName: ROPE;
fileName: ROPE;
Append[FALSE, "Searching libraries for undefined UNIX symbol %g.", LIST[[rope[missing.first]]]];
TRUSTED {found ¬ LibSearch.SymFind[missingU, @libfd, @liboffset, @magic, @modulenameU]};
IF found = LibSearch.False
THEN {
Append[TRUE, "Cannot resolve UNIX-level unbound symbol %g", LIST[[rope[missing.first]]]];
dont ¬ TRUE;
}
ELSE {
moduleName ¬ UXStrings.ToRope[modulenameU];
Append[FALSE, "Loading library module %g.", LIST[[rope[moduleName]]]];
fileName ¬ moduleName.Substr[len: moduleName.SkipTo[skip: "("]];
IF TestErr[
ile:
IL.IncrementalLoadFile[
fName: UXStrings.Create[fileName],
fOffset: liboffset,
patchSizeProc: PatchSizeCProc[],
patchSizeClientData: PatchSizeClientData[byteSize: patchByteSize]],
from: IO.PutFR1["LibLoad[%g]", [rope[moduleName]] ]]
THEN GOTO Abort;
};
missing ¬ missing.rest;
ENDLOOP;
commitPulses ¬ BasicTime.GetClockPulses[];
intermediate attempts to CommitIncrementalLoad accrue to libPulses whereas commitPulses reports only the time for the last CommitIncrementalLoad
ENDLOOP;
libPulses ¬ (tmpPulses ¬ BasicTime.GetClockPulses[]) - libPulses;
commitPulses ¬ tmpPulses - commitPulses;
END;
IF dont
THEN {
Append[TRUE, "%g (s=%g m=%g) not run becaue of unbound UNIX-level symbols", LIST[[rope[PFS.RopeFromPath[torun]]], [cardinal[thisSize]], [time[thisTime]]]];
GOTO Abort;
};
install ¬ LookupText["←XR←install←", moduleName];
IF loadOnly
THEN start ¬
NIL
ELSE {
start ¬ LookupText["←XR←run←", moduleName];
IF start=NIL THEN start ¬ LookupText["←XR←run", NIL];
};
EXITS
Abort => {
ilPulses ¬ commitPulses ¬ 0;
[] ¬ TestErr[IL.AbortIncrementalLoad[], "Abort"];
};
};
Start of body
LibSearch.Crock1[];
IF torun=
NIL
THEN {
Append[TRUE, "Hint \"%q\" and patterns %g don't lead to any existing non-empty file", LIST[[rope[given]], [rope[FmtHacks[searchResult.hacks]]]]];
RETURN[error: TRUE];
};
Process.CheckForAbort[];
IF TestErr[IL.LockIncrementalLoadState[TRUE], "Lock"] THEN RETURN[error: TRUE];
Inner[ ! UNWIND => [] ¬ TestErr[IL.UnlockIncrementalLoadState[], "UNWIND Unlock"]];
[] ¬ TestErr[IL.UnlockIncrementalLoadState[], "Unlock"];
IF error OR dont THEN RETURN;
hasInstall ¬ install#NIL AND thisFile.seqNum = install.fe.seqNum;
hasStart ¬ start#NIL AND thisFile.seqNum = start.fe.seqNum;
IF hasInstall
THEN {
ENABLE UNWIND => MesaLoadState.AbortInstallation[];
installFn: InstallFunc ~ CProcToCedar[install.value];
problems: LIST OF MesaLoadState.InstallationProblem ¬ NIL;
abort: BOOL ¬ FALSE;
Report: MesaLoadState.ReportProc ~
CHECKED {
WITH problem
SELECT
FROM
ui: MesaLoadState.InstallationProblem.unboundImport => {
ifName: ROPE ~ MesaLoadState.InterfaceName[ui.interface];
IF unboundOK.Fetch[ifName].found THEN RETURN;
};
re: MesaLoadState.InstallationProblem.reExport => {
IF runEvenIfAlreadyRun THEN RETURN;
};
tc: MesaLoadState.InstallationProblem.typeClash => {
type clashes are unsafe; there should be a switch to allow the the user to proceed
abort ¬ TRUE;
};
ENDCASE => NULL;
problems ¬ CONS[problem, problems];
};
installPulses ¬ BasicTime.GetClockPulses[];
MesaLoadState.BeginInstallation[]; -- will raise RuntimeError.UnboundProcedureFault if new installation support is not present.
installFn[ ! RegisterRefLiteral.UnknownType =>
CHECKED {
abort ¬ TRUE;
IO.PutRope[cmd.err, "Unable to load "];
IO.PutRope[cmd.err, name];
IO.PutRope[cmd.err, ", probably because of a version mismatch with Rope\n"];
CONTINUE;
}];
IF NOT abort THEN MesaLoadState.CheckInstallation[Report];
IF problems#
NIL
THEN {
InstallationComforts.PutProblems[cmd.err, problems];
IO.PutChar[cmd.err, '\n];
};
IF abort
THEN { MesaLoadState.AbortInstallation[]; RETURN [error: TRUE] }
ELSE { MesaLoadState.CommitInstallation[] };
installPulses ¬ BasicTime.GetClockPulses[] - installPulses;
hasInstall ¬ hasInstall
};
IF hasStart
THEN {
runFn: InstallFunc ~ CProcToCedar[start.value];
runPulses ¬ BasicTime.GetClockPulses[];
[] ¬ SymTab.Store[workingDirectories, moduleName, PFS.GetWDir[]]; -- remember working directories of things that get started.
runFn[ ! ABORTED => {wasAborted ¬ TRUE; CONTINUE}]; -- catch ABORTED so that if there are messages from loading they'll get printed even if starting gets an error (e.g., an unbound procedure fault).
runPulses ¬ BasicTime.GetClockPulses[] - runPulses;
hasStart ¬ hasStart;
};
[] ¬ SymTab.Store[runInfoTab, unixName, NEW[RunInfo ¬ [unixName: unixName, torun: torun, moduleName: moduleName, hasInstall: hasInstall, hasStart: hasStart]]];
SELECT
TRUE
FROM
hasInstall AND hasStart AND ~loadOnly => Bitch["Ran %g (s=%g m=%g)"];
hasInstall AND ~hasStart AND ~loadOnly => Bitch["Loaded and installed %g (s=%g m=%g) (it has no start proc)"];
hasInstall AND loadOnly => Bitch["Loaded and installed %g (s=%g m=%g)"];
~hasInstall AND hasStart AND ~loadOnly => Bitch["Loaded and started %g (s=%g m=%g) (it has no install proc)"];
~hasInstall AND ~hasStart AND ~loadOnly => Bitch["Loaded %g (s=%g m=%g) (it has no install or start proc)"];
~hasInstall AND loadOnly => Bitch["Loaded %g (s=%g m=%g) (it has no install proc)"];
ENDCASE => ERROR --the above covers all cases--;
IF printTimings THEN Append[FALSE, "\t(find=%5.2f il=%5.2f lib=%5.2f c=%5.2f inst=%5.2f run=%5.2f)", LIST[ [real[Seconds[findPulses]]], [real[Seconds[ilPulses]]], [real[Seconds[libPulses]]], [real[Seconds[commitPulses]]], [real[Seconds[installPulses]]], [real[Seconds[runPulses]]] ]];
IF wasAborted THEN ERROR ABORTED; -- propagate abort
RETURN;
};
RunCmd:
PROC [cmd: Commander.Handle]
RETURNS [result:
REF ¬
NIL, msg:
ROPE ¬
NIL]
--Commander.CommandProc-- =
TRUSTED {
ENABLE QuotedStringError => {msg ¬ "Mismatched quotes"; GO TO Failure};
dontStartIfUnbound, runAgain, loadOnly, excepting, quote, printTimings: BOOL ¬ FALSE;
preferOptimized: BOOL ¬ TRUE;
unboundOK: SymTab.Ref ~ SymTab.Create[case: TRUE]; -- never used!
something: BOOL ¬ FALSE;
patchByteSize: CARD ¬ 0;
cmds: IO.STREAM ¬ NIL;
defaultSwitches: ROPE ¬ GlobalRunDefaults.GetGlobalRunDefaults[];
WITH ProcessProps.GetProp[$RunDefaultSwitches]
SELECT
FROM
rope: ROPE => IF Rope.Size[rope] > 0 AND Rope.Fetch[rope, 0] = '- THEN defaultSwitches ¬ rope;
ENDCASE => NULL;
cmds ¬ IO.RIS[Rope.Concat[defaultSwitches, cmd.commandLine]];
FOR arg:
ROPE ¬ GetCmdToken[cmds], GetCmdToken[cmds]
UNTIL arg =
NIL
DO
error: BOOL ¬ FALSE;
IF Rope.Size[arg] > 0
AND Rope.Fetch[arg, 0] = '-
THEN {
newsense: BOOL ¬ TRUE;
FOR j:
INT
IN [1..Rope.Length[arg])
DO
sense: BOOL ~ newsense; newsense ¬ TRUE;
SELECT Rope.Fetch[arg, j]
FROM
'd => dontStartIfUnbound ¬ sense;
'a => runAgain ¬ sense;
'l => loadOnly ¬ sense;
'p => {
FollowingCARD:
PROCEDURE [stream:
IO.
STREAM]
RETURNS [
CARD] ~
TRUSTED {
arg: Rope.ROPE ~ GetCmdToken[stream];
card: CARD ¬ 0;
{
try:
CARD ~ Convert.CardFromRope[r: arg
! Convert.Error => GO TO error];
card ¬ try;
EXITS
error => cmd.out.PutF1["Invalid Run switch: -p %g\n", [rope[arg]]];
};
RETURN [card];
};
patchByteSize ¬ FollowingCARD[stream: cmds];
};
'q => quote ¬ sense;
't => printTimings ¬ sense;
'u => excepting ¬ sense;
'x => excepting ¬ NOT sense;
'o => preferOptimized ¬ sense;
'~ => newsense ¬ NOT sense;
ENDCASE => cmd.out.PutF1["Invalid Run switch: %g\n", [character[Rope.Fetch[arg, j]]]];
ENDLOOP;
LOOP;
};
IF excepting AND Rope.SkipTo[s: arg, skip: "[]<>/."] < Rope.Length[arg] THEN excepting ¬ FALSE;
IF excepting THEN { [] ¬ SymTab.Store[unboundOK, arg, $T]; LOOP };
[error: error] ¬ RunInternal[cmd: cmd, name: arg, unboundOK: unboundOK, runEvenIfAlreadyRun: runAgain, runEvenIfUnbound: NOT dontStartIfUnbound, loadOnly: loadOnly, quote: quote, preferOptimized: preferOptimized, printTimings: printTimings, patchByteSize: patchByteSize];
IF error THEN GOTO Failure;
something ¬ TRUE;
IF NOT Rope.IsEmpty[msg] THEN { IO.PutRope[cmd.err, msg]; msg ¬ NIL };
ENDLOOP;
IF excepting THEN { msg ¬ "Looks like you forgot your -x"; GOTO Failure };
IF NOT something THEN { msg ¬ runUsage; GOTO Failure };
EXITS Failure => result ¬ $Failure;
};
LoadedFilesCmd:
PROC [cmd: Commander.Handle]
RETURNS [result:
REF ¬
NIL, msg:
ROPE ¬
NIL]
--Commander.CommandProc-- = {
ENABLE {
QuotedStringError => {msg ¬ "Mismatched quotes"; GO TO Failure};
PFS.Error => IF error.group = user THEN {msg ¬ error.explanation; GO TO Failure};
};
cmds: IO.STREAM ~ IO.RIS[cmd.commandLine];
GetFilePattern:
PROC
RETURNS [
ROPE] ~ {
RETURN [Rope.Cat["*/", GetCmdToken[cmds], "*"]]
};
exceptions: LIST OF ROPE ¬ NIL;
Exception:
PROC [rope:
ROPE]
RETURNS [
BOOL] ~ {
FOR tail:
LIST
OF
ROPE ¬ exceptions, tail.rest
UNTIL tail =
NIL
DO
IF Rope.Match[tail.first, rope, FALSE] THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
format: { pcr, command, names, ccode, libs } ¬ command;
first: ROPE ¬ NIL;
after: ROPE ¬ NIL;
before: ROPE ¬ NIL;
last: ROPE ¬ NIL;
list: LIST OF ROPE ¬ NIL;
something: BOOL ¬ FALSE;
libTab: SymTab.Ref ¬ NIL;
FOR target:
ROPE ¬ GetCmdToken[cmds], GetCmdToken[cmds]
UNTIL target =
NIL
DO
Match: PROC [x: ROPE] RETURNS [BOOL] = { RETURN [Rope.Match[x, target, FALSE]] };
SELECT
TRUE
FROM
Match["-pcr"] => format ¬ pcr;
Match["-command"] => format ¬ command;
Match["-names"] => format ¬ names;
Match["-ccode"] => format ¬ ccode;
Match["-libs"] => format ¬ libs;
Match["-first"] => first ¬ GetFilePattern[];
Match["-after"] => after ¬ GetFilePattern[];
Match["-before"] => before ¬ GetFilePattern[];
Match["-last"] => last ¬ GetFilePattern[];
Match["-except"] => exceptions ¬ CONS[GetFilePattern[], exceptions];
ENDCASE => {msg ¬ loadedFilesUsage; GO TO Failure};
something ¬ TRUE;
ENDLOOP;
IF NOT something THEN {msg ¬ loadedFilesUsage; GO TO Failure};
IF first = NIL AND after = NIL THEN first ¬ "*";
IF before = NIL AND last = NIL THEN last ¬ " ";
TRUSTED {
FOR fe:
IL.FileEntry ¬
IL.GetPrevFileEntry[
NIL],
IL.GetPrevFileEntry[fe]
UNTIL fe =
NIL
DO
rope: ROPE ~ UXStrings.ToRope[fe.fName];
IF Rope.Size[rope] # 0 THEN list ¬ CONS[rope, list];
ENDLOOP;
};
UNTIL list =
NIL
OR (first #
NIL
AND Rope.Match[first, list.first,
FALSE])
DO
this: ROPE ¬ list.first;
list ¬ list.rest;
IF after # NIL AND Rope.Match[after, this, FALSE] THEN EXIT;
ENDLOOP;
IF
NOT (before =
NIL
AND last =
NIL)
THEN {
FOR tail:
LIST
OF
ROPE ¬ list, tail.rest
UNTIL tail =
NIL
DO
SELECT
TRUE
FROM
(last # NIL AND Rope.Match[last, tail.first, FALSE]) => {tail.rest ¬ NIL};
(tail.rest#NIL AND before#NIL AND Rope.Match[before, tail.rest.first, FALSE]) => {tail.rest ¬ NIL};
ENDCASE => NULL;
ENDLOOP;
};
FOR tail:
LIST
OF
ROPE ¬ list, tail.rest
UNTIL tail =
NIL
DO
IF
NOT Exception[tail.first]
THEN
SELECT format
FROM
pcr => {
moduleName: ROPE ~ GetModuleName[tail.first];
SELECT
TRUE
FROM
(moduleName #
NIL) => {
IO.PutRope[cmd.out, "LoadAndRun "];
IO.PutRope[cmd.out, tail.first];
IO.PutRope[cmd.out, " "];
IO.PutRope[cmd.out, moduleName];
IO.PutRope[cmd.out, "\l"];
};
Rope.Match["*.a*", tail.first, TRUE] => {};
ENDCASE => {
IO.PutRope[cmd.out, "UnixLoad "];
IO.PutRope[cmd.out, tail.first];
IO.PutRope[cmd.out, "\l"];
};
};
command => {
IO.PutRope[cmd.out, "Run -q "];
IO.PutRope[cmd.out, PFS.RopeFromPath[PathFromUnixName[tail.first]]];
IO.PutRope[cmd.out, "\n"];
};
names => {
SELECT
TRUE
FROM
Rope.Match["*.a*", tail.first, TRUE] => {};
ENDCASE => {
IO.PutRope[cmd.out, tail.first];
IO.PutRope[cmd.out, " "];
};
};
libs => {
IF Rope.Match["*.a*", tail.first,
TRUE]
THEN {
IF libTab = NIL THEN libTab ¬ SymTab.Create[];
IF SymTab.Insert[libTab, tail.first, tail.first]
THEN {
IO.PutRope[cmd.out, tail.first];
IO.PutRope[cmd.out, " "];
};
};
};
ccode => {
moduleName: ROPE ~ GetModuleName[tail.first];
IF moduleName #
NIL
THEN {
val: REF ~ SymTab.Fetch[workingDirectories, moduleName].val;
IF LookupText["←XR←install←", moduleName] #
NIL
THEN {
IO.PutF1[cmd.out, " XR←install←%g();\l", [rope[moduleName]]];
IO.PutRope[cmd.out, " XR𡤌ommitInstallation();\l"];
};
IF LookupText["←XR←run←", moduleName] #
NIL
THEN {
WITH val
SELECT
FROM
path:
PATH => {
IO.PutF[cmd.out,
" {extern void XR←run←%g(); XRllWithPFSWorkingDirectory(XR←run←%g, \"%g\");}\l", [rope[moduleName]], [rope[moduleName]], [rope[PFS.RopeFromPath[path]]]];
};
ENDCASE => {
IO.PutF1[cmd.out, " XR←run←%g();\l", [rope[moduleName]]];
};
};
};
};
ENDCASE => ERROR;
ENDLOOP;
EXITS
Failure => result ¬ $Failure;
};