-- MDSubrImpl.Mesa
-- last edit January 4, 1983 1:30 pm
-- last edit May 22, 1983 4:49 pm, Russ Atkinson
-- Pilot 6.0/ Mesa 7.0

DIRECTORY
CWF: TYPE USING [FWF0, FWF1, SWF1, SWF2, SWF3, WF0, WF1, WF2],
LongString: TYPE USING [EquivalentString],
IO: TYPE USING[PutChar],
MDSubr: TYPE USING [AddToLook, entNil, Entry, EntrySeq, HUGEDEPLIST, Look, lookNil,
 LookSeq, LookupOtherSym, SMALLDEPLIST, StringSeq, WalkTheGraph],
Stream: TYPE USING [Handle, PutChar],
String: TYPE USING [AppendString],
Subr: TYPE USING [CopyString, EndsIn, FreeString, PackedTime,
 strcpy, SubStrCopy, TTYProcs],
Time: TYPE USING [Current];

MDSubrImpl: PROGRAM
IMPORTS CWF, IO, LongString, MDSubr, Stream, String, Subr, Time
EXPORTS MDSubr = {

-- no MDS USAGE !!!

-- utilities

StripFirstWord: PUBLIC PROC[str, result: LONG STRING] = {
i: CARDINAL ← 0;
WHILE i < str.length AND i <result.maxlength DO
IF str[i] = ' THEN EXIT;
 result[i] ← str[i];
 i ← i + 1;
ENDLOOP;
result.length ← i;
Subr.SubStrCopy[str,str,i];
};

-- having to do with EntrySeq's
PrintEntries: PUBLIC PROC[entryseq: MDSubr.EntrySeq, sh: Stream.Handle,
 stringseq: MDSubr.StringSeq, ttyhandle: Subr.TTYProcs] = {
i: CARDINAL;
j: CARDINAL;
ent, entchild: MDSubr.Entry;
stemp: STRING ← [200];
str: LONG STRING;
date: Subr.PackedTime ← Time.Current[];

WFsh: PROC[ch: CHAR] = {
IF sh = NIL THEN ttyhandle.out.PutChar[ch]
ELSE Stream.PutChar[sh, ch];
 };

CWF.FWF1[WFsh, "-- created %lt\n"L, @date];
IF (str ← MDSubr.LookupOtherSym["PATH"L, stringseq]) ~= NIL THEN
CWF.FWF1[WFsh, "PATH = %s\n"L, str];
IF (str ← MDSubr.LookupOtherSym["MESACOMP"L, stringseq]) ~= NIL THEN
CWF.FWF1[WFsh, "MESACOMP = %s\n"L, str];
IF (str ← MDSubr.LookupOtherSym["MESABIND"L, stringseq]) ~= NIL THEN
CWF.FWF1[WFsh, "MESABIND = %s\n"L, str];
FOR i IN [0.. stringseq.size) DO
IF LongString.EquivalentString[stringseq[i].val, "local"L] THEN
  CWF.FWF1[WFsh, "%s = local\n"L, stringseq[i].str];
ENDLOOP;
CWF.FWF0[WFsh,"\n"L];
FOR i IN [0..entryseq.size) DO
 ent ← @entryseq[i];
IF ent.rule = NIL AND ent.depends[0] = MDSubr.entNil THEN LOOP;
-- CWF.WF3["Entry %d: '%s', Rule '%s'\n"L,@i,ent.name,ent.rule];
CWF.FWF1[WFsh,"%s:"L,ent.name];
 j ← 0;
DO
  IF ent.depends[j] = MDSubr.entNil THEN EXIT;
  -- WF.WF1[" Depends on %d\n"L,@ent.depends[j]];
  entchild ← @entryseq[ent.depends[j]];
  CWF.FWF1[WFsh," %s"L,entchild.name];
  j ← j + 1;
  ENDLOOP;
CWF.FWF0[WFsh,"\n"L];
IF ent.rule ~= NIL THEN CWF.FWF1[WFsh,"= %s\n"L,ent.rule];
CWF.FWF0[WFsh,"\n"L];
ENDLOOP;
};

GetAnEntry: PUBLIC PROC[name: LONG STRING, entryseq: MDSubr.EntrySeq]
RETURNS[i: CARDINAL, new: BOOL] = {
FOR i IN [0..entryseq.size) DO
IF LongString.EquivalentString[name,entryseq[i].name] THEN
  RETURN[i,FALSE];
ENDLOOP;
IF entryseq.size >= entryseq.maxsize THEN {
 m: CARDINAL ← entryseq.maxsize;
CWF.WF1["Error - too many dependency entries > %d\n"L,@m]
 }
ELSE {
-- other fields are defaulted
 entryseq[entryseq.size] ← [name: Subr.CopyString[name, entryseq.entryzone],
  depends: DESCRIPTOR[NIL, 0]];
 entryseq[entryseq.size].depends ← DESCRIPTOR[entryseq.entryzone.NEW[
  ARRAY[0..MDSubr.SMALLDEPLIST) OF CARDINAL], MDSubr.SMALLDEPLIST];
 entryseq[entryseq.size].depends[0] ← MDSubr.entNil;
 entryseq.size ← entryseq.size + 1;
 };
RETURN[entryseq.size-1,TRUE];
};

AddToDepends: PUBLIC PROC[parent, child: CARDINAL, entryseq: MDSubr.EntrySeq] = {
i, max, len, inx: CARDINAL;
saved: LONG DESCRIPTOR FOR ARRAY OF CARDINAL;
entry: MDSubr.Entry;
temp: CARDINAL;
p: LONG POINTER;
IF parent = MDSubr.entNil THEN RETURN;
i ← 0;
len ← LENGTH[entryseq[parent].depends];
max ← len - 1;
entry ← @entryseq[parent];
WHILE i < max DO
 inx ← entry.depends[i];
IF inx = child THEN RETURN; -- already there
IF inx = MDSubr.entNil THEN EXIT;
 i ← i + 1;
REPEAT
FINISHED => {
  -- CWF.WF1["Extending %s\n"L, entry.name];
  IF len >= MDSubr.HUGEDEPLIST THEN {
   CWF.WF0["Error - too many depends\n"L];
   RETURN;
   };
  saved ← entry.depends;
  p ← entryseq.entryzone.NEW[ARRAY[0..MDSubr.HUGEDEPLIST) OF CARDINAL];
  entry.depends ← DESCRIPTOR[p,MDSubr.HUGEDEPLIST];
  FOR i IN [0..LENGTH[saved]) DO
   temp ← saved[i];
   entry.depends[i] ← temp;
   ENDLOOP;
  i ← max;
  p ← BASE[saved];
  -- no frees allowed
  -- longzone.FREE[@p];
  };
ENDLOOP;
entry.depends[i] ← child;
entry.depends[i+1] ← MDSubr.entNil;
};

FreeEntrySeq: PUBLIC PROC[pentryseq: LONG POINTER TO MDSubr.EntrySeq] = {
entry: MDSubr.Entry;
longp: LONG POINTER;

IF pentryseq^ = NIL THEN RETURN;
IF FALSE THEN
-- no frees allowed
FOR i: CARDINAL IN [0..pentryseq^.size) DO
 entry ← @(pentryseq^)[i];
 Subr.FreeString[entry.name];
 Subr.FreeString[entry.rule];
 longp ← BASE[entry.depends];
-- IF longp ~= NIL THEN longzone.FREE[@longp];
 entry^ ← [depends: DESCRIPTOR[NIL,0]];
ENDLOOP;
-- longzone.FREE[pentryseq];
pentryseq^ ← NIL;
};


-- proc is only called once for each node that is connected to firstinx
WalkTheGraph: PUBLIC PROC[firstinx: CARDINAL, entryseq:MDSubr.EntrySeq,
 proc: PROC[MDSubr.Entry]] = {

RecurseThru: PROC[inx: CARDINAL, entryseq:MDSubr.EntrySeq,
  proc: PROC[MDSubr.Entry]] = {
 i: CARDINAL;
 entry: MDSubr.Entry ← @entryseq[inx];
IF entry.visited THEN RETURN;
 proc[entry]; -- call the users proc
 entry.visited ← TRUE;
 i ← 0;
WHILE entry.depends[i] ~= MDSubr.entNil DO
  -- the recursive call
  RecurseThru[entry.depends[i], entryseq, proc];
  i ← i + 1;
  ENDLOOP;
 };

FOR i: CARDINAL IN [0 .. entryseq.size) DO
 entryseq[i].visited ← FALSE;
ENDLOOP;
RecurseThru[firstinx, entryseq, proc];
};

-- look for a specific case of a .bcd depending on both a .config
-- and .mesa file
ThrowAwayLeaves: PUBLIC PROC[firstinx: CARDINAL, entryseq: MDSubr.EntrySeq,
 lookseq: MDSubr.LookSeq, ismodel: BOOL] = {

RecurThrowAwayLeaves: PROC[entry: MDSubr.Entry] = {
 i, j1, j2: CARDINAL;
 lookj1, lookj2: MDSubr.Look;
IF ismodel AND Subr.EndsIn[entry.name, ".bcd"L] THEN {
  i ← 0;
  lookj1 ← NIL;
  WHILE (j1 ← entry.depends[i]) ~= MDSubr.entNil DO
   IF (Subr.EndsIn[entryseq[j1].name,".config"L]
   OR Subr.EndsIn[entryseq[j1].name, ".mesa"L]) THEN {
    lookj1 ← @lookseq[entryseq[j1].lookinx];
    IF lookj1.presentonlocaldisk THEN RETURN;
    };
   i ← i + 1;
   ENDLOOP;
  -- if there was a .mesa or .config, and not on disk, then
  -- throw out
  IF lookj1 ~= NIL THEN {
   entry.depends[0] ← MDSubr.entNil;
   entry.rule ← NIL
   };
  }
ELSE {
  IF Subr.EndsIn[entry.name, ".bcd"L]
  AND (j1 ← entry.depends[0]) ~= MDSubr.entNil
  AND (j2 ← entry.depends[1]) ~= MDSubr.entNil
  AND entry.depends[2] = MDSubr.entNil
  AND Subr.EndsIn[entryseq[j1].name,".config"L]
  AND Subr.EndsIn[entryseq[j2].name, ".mesa"L] THEN {
   lookj1 ← @lookseq[entryseq[j1].lookinx];
   lookj2 ← @lookseq[entryseq[j2].lookinx];
   IF NOT lookj1.presentonlocaldisk
   AND NOT lookj2.presentonlocaldisk THEN
     entry.depends[0] ← MDSubr.entNil
   ELSE IF lookj1.presentonlocaldisk
    AND NOT lookj2.presentonlocaldisk THEN
    entry.depends[1] ← MDSubr.entNil
   ELSE IF NOT lookj1.presentonlocaldisk
    AND lookj2.presentonlocaldisk THEN {
     entry.depends[0] ← j2;
    entry.depends[1] ← MDSubr.entNil
    };
   };
  };
 };
FOR i: CARDINAL IN [0.. entryseq.size) DO
 entryseq[i].visited ← FALSE; 
ENDLOOP;
MDSubr.WalkTheGraph[firstinx, entryseq, RecurThrowAwayLeaves];
};


CheckNames: PUBLIC PROC[entryseq: MDSubr.EntrySeq, lookseq: MDSubr.LookSeq] = {
look: MDSubr.Look;
header: BOOLFALSE;
FOR i: CARDINAL IN [0..entryseq.size) DO
IF entryseq[i].lookinx ~= MDSubr.lookNil THEN {
  look ← @lookseq[entryseq[i].lookinx];
  IF NOT LongString.EquivalentString[look.name, entryseq[i].name]
  THEN {
   IF NOT header THEN {
    CWF.WF0["Errors in look vs entry:\n"L];
    header ← TRUE;
    };
   CWF.WF2["Err - look %s entry %s\n"L, look.name,
     entryseq[i].name];
   };
  };
ENDLOOP;
-- CWF.WF0["Looks ok to me\n"L];
};

ConvertToLook: PUBLIC PROC[entryseq: MDSubr.EntrySeq, lookseq: MDSubr.LookSeq] = {
FOR i: CARDINAL IN [0..entryseq.size) DO
IF entryseq[i].lookinx = MDSubr.lookNil THEN {
  entryseq[i].lookinx ← lookseq.size;
  [] ← MDSubr.AddToLook[entryseq[i].name, lookseq];
  };
ENDLOOP;
};

InsertOtherSym: PUBLIC PROC[str, val: LONG STRING, stringseq: MDSubr.StringSeq] = {
IF stringseq.size >= stringseq.maxsize THEN CWF.WF0["too many other syms\n"L]
ELSE {
 stringseq[stringseq.size].str ← Subr.CopyString[str];
 stringseq[stringseq.size].val ← Subr.CopyString[val];
 stringseq.size ← stringseq.size + 1;
 };
RETURN;
};

-- return the value corresponding to "str"
-- searches backwards to allow users to redefine the strings
LookupOtherSym: PUBLIC PROC[str: LONG STRING, stringseq: MDSubr.StringSeq]
RETURNS[LONG STRING] = {
FOR i: CARDINAL DECREASING IN [0..stringseq.size) DO
IF LongString.EquivalentString[str,stringseq[i].str] THEN
  RETURN[stringseq[i].val];
ENDLOOP;
RETURN[NIL];
};


GetCompilerRule: PUBLIC PROC[stringseq: MDSubr.StringSeq, name: LONG STRING,
 zone: UNCOUNTED ZONE] RETURNS[LONG STRING] = {
comprule: STRING ← [100];
str: LONG STRING;
pat: STRING ← [100];
count: CARDINAL ← 0;
str ← MDSubr.LookupOtherSym["PILOTMESACOMP"L, stringseq];
IF str = NIL THEN
 str ← MDSubr.LookupOtherSym["MESACOMP"L, stringseq];
Subr.strcpy[pat, str];
FOR i: CARDINAL IN [0..pat.length) DO
IF pat[i] = '% THEN count ← count + 1;
ENDLOOP;
IF count = 0 THEN String.AppendString[pat, " %s"L];
IF count > 3 THEN CWF.WF1["Too many %% in '%s'\n"L, pat]
ELSE IF count = 3 THEN CWF.SWF3[comprule, pat, name, name, name]
ELSE IF count = 2 THEN CWF.SWF2[comprule, pat, name, name]
ELSE CWF.SWF1[comprule, pat, name];
RETURN[Subr.CopyString[comprule, zone]];
};

GetBinderRule: PUBLIC PROC[stringseq: MDSubr.StringSeq, name: LONG STRING,
 zone: UNCOUNTED ZONE] RETURNS[LONG STRING] = {
comprule: STRING ← [100];
str: LONG STRING;
pat: STRING ← [100];
count: CARDINAL ← 0;
i: CARDINAL;
str ← MDSubr.LookupOtherSym["PILOTMESABIND"L, stringseq];
IF str = NIL THEN
 str ← MDSubr.LookupOtherSym["MESABIND"L, stringseq];
Subr.strcpy[pat, str];
FOR i IN [0..pat.length) DO
IF pat[i] = '% THEN count ← count + 1;
ENDLOOP;
IF count = 0 THEN String.AppendString[pat, " %s"L];
IF count > 3 THEN CWF.WF1["Too many %% in '%s'\n"L, pat]
ELSE IF count = 3 THEN CWF.SWF3[comprule, pat, name, name, name]
ELSE IF count = 2 THEN CWF.SWF2[comprule, pat, name, name]
ELSE CWF.SWF1[comprule, pat, name];
RETURN[Subr.CopyString[comprule, zone]];
};



}.

IDEAS

1. comments in line.cm for error messages