-- ListerRoutines.mesa; 
--   edited by Sandman; October 14, 1980  11:06 AM
--   edited by Sweet;  3-Sep-80 16:12:54

DIRECTORY
  AltoDefs USING [PageCount],
  BcdDefs,
  BcdOps,
  CommanderDefs USING [InitCommander, WaitCommands],
  ControlDefs USING [CSegPrefix],
  ImageDefs USING [BcdTime],
  InlineDefs USING [LowHalf],
  IODefs USING [NumberFormat, TAB],
  ListerDefs: FROM "listerdefs",
  OutputDefs USING [PutChar, PutCR, PutNumber, PutString, PutTime],
  SegmentDefs USING [
    DefaultVersion, DeleteFileSegment, FileHandle, FileSegmentAddress,
    FileSegmentHandle, MoveFileSegment, NewFile, NewFileSegment, Read, SwapIn,
    Unlock],
  String,
  Symbols USING [
    bodyType, ctxType, HTIndex, HTNull, htType, ISEIndex, mdType, SENull, seType,
    ssType],
  SymbolSegment USING [extType, ltType, treeType],
  SymbolTable USING [Base],
  Storage USING [Node, Free],
  Table USING [Base, Index, Notifier, Selector],
  TimeDefs USING [AppendDayTime, UnpackDT],
  Tree USING [Node];

ListerRoutines: PROGRAM
  IMPORTS
    BcdOps, CommanderDefs, OutputDefs, ImageDefs, SegmentDefs, String, Storage,
    InlineDefs, TimeDefs
  EXPORTS ListerDefs, Table
  SHARES SymbolTable =PUBLIC
  
  BEGIN OPEN OutputDefs;
  
  NumberFormat: TYPE = IODefs.NumberFormat;
  PageCount: TYPE = AltoDefs.PageCount;
  
  IncorrectVersion: SIGNAL = CODE;
  NoFGT: SIGNAL = CODE;
  NoCode: SIGNAL = CODE;
  NoSymbols: SIGNAL = CODE;
  MultipleModules: SIGNAL = CODE;
  version, creator, source: BcdDefs.VersionStamp;
  Dstar: BOOLEAN;
  filename: STRING;
  
  symbols: SymbolTable.Base;
  bases: PRIVATE ARRAY [0..16) OF Table.Base;
  
  SetRoutineSymbols: PROCEDURE [s: SymbolTable.Base] =
    BEGIN OPEN s.stHandle;
    symbase: Table.Base ← InlineDefs.LowHalf[s.stHandle];
    symbols ← s;
    bases[SymbolSegment.treeType] ← symbase + treeBlock.offset;
    bases[Symbols.seType] ← symbase + seBlock.offset;
    bases[Symbols.htType] ← symbase + htBlock.offset;
    bases[Symbols.ssType] ← symbase + ssBlock.offset;
    bases[Symbols.ctxType] ← symbase + ctxBlock.offset;
    bases[Symbols.mdType] ← symbase + mdBlock.offset;
    bases[Symbols.bodyType] ← symbase + bodyBlock.offset;
    bases[SymbolSegment.ltType] ← symbase + litBlock.offset;
    bases[SymbolSegment.extType] ← symbase + extBlock.offset;
    UpdateBases[];
    END;
    
  -- Tree
  
  NotifyNode: TYPE = RECORD [
    notifier: Table.Notifier, link: POINTER TO NotifyNode];
  
  notifyList: POINTER TO NotifyNode ← NIL;
  
  AddNotify: PUBLIC PROCEDURE [proc: Table.Notifier] =
    BEGIN
    p: POINTER TO NotifyNode = Storage.Node[SIZE[NotifyNode]];
    p↑ ← [notifier: proc, link: notifyList];
    notifyList ← p;
    proc[DESCRIPTOR[bases]];
    RETURN
    END;
    
  DropNotify: PUBLIC PROCEDURE [proc: Table.Notifier] =
    BEGIN
    p, q: POINTER TO NotifyNode;
    IF notifyList = NIL THEN RETURN;
    p ← notifyList;
    IF p.notifier = proc THEN notifyList ← p.link
    ELSE
      BEGIN
      DO
	q ← p;
	p ← p.link;
	IF p = NIL THEN RETURN;
	IF p.notifier = proc THEN EXIT
	ENDLOOP;
      q.link ← p.link;
      END;
    Storage.Free[p];
    RETURN
    END;
    
  UpdateBases: PROCEDURE =
    BEGIN
    p: POINTER TO NotifyNode;
    FOR p ← notifyList, p.link UNTIL p = NIL DO
      p.notifier[DESCRIPTOR[bases]] ENDLOOP;
    RETURN
    END;
    
  -- to make TreeInit happy
  
  
  GetChunk: PROCEDURE [size: CARDINAL] RETURNS [Table.Index] =
    BEGIN
    IF size # SIZE[Tree.Node] THEN ERROR; -- called to reserve empty
    RETURN[LOOPHOLE[0]];
    END;
    
  -- to make LiteralPack.Initialize happy
  
  
  Bounds: PROCEDURE [table: Table.Selector]
    RETURNS [base: Table.Base, size: CARDINAL] =
    BEGIN OPEN symbols.stHandle;
    SELECT table FROM
      SymbolSegment.treeType => RETURN[bases[table], treeBlock.size];
      Symbols.seType => RETURN[bases[table], seBlock.size];
      Symbols.htType => RETURN[bases[table], htBlock.size];
      Symbols.ssType => RETURN[bases[table], ssBlock.size];
      Symbols.ctxType => RETURN[bases[table], ctxBlock.size];
      Symbols.mdType => RETURN[bases[table], mdBlock.size];
      Symbols.bodyType => RETURN[bases[table], bodyBlock.size];
      SymbolSegment.ltType => RETURN[bases[table], litBlock.size];
      SymbolSegment.extType => RETURN[bases[table], extBlock.size];
      ENDCASE => ERROR;
    END;
    
  LoadFromConfig: PROCEDURE [
    configName, moduleName: STRING, saveBcdSeg: BOOLEAN ← FALSE]
    RETURNS [
      code, symbols, bcdseg: SegmentDefs.FileSegmentHandle,
      mti: BcdDefs.MTIndex] =
    BEGIN OPEN BcdDefs, SegmentDefs;
    bcd: POINTER TO BcdDefs.BCD;
    sgb, mtb, ftb: BcdDefs.Base;
    ssb: BcdOps.NameString;
    pages: AltoDefs.PageCount;
    configFile, codeFile, symsFile: FileHandle;
    codeFileName: STRING ← [40];
    symsFileName: STRING ← [40];
    mh: BcdOps.MTHandle;
    sfi, cfi: BcdDefs.FTIndex;
    ss1, ss2: String.SubStringDescriptor;
    
    CheckModule: PROC [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
      RETURNS [BOOLEAN] =
      BEGIN
      ss2.offset ← mth.name;
      ss2.length ← ssb.size[mth.name];
      RETURN[String.EquivalentSubStrings[@ss1, @ss2]];
      END;
      
    ss1 ← [base: moduleName, offset: 0, length: moduleName.length];
    code ← symbols ← NIL;
    Dstar ← FALSE;
    filename ← configName;
    codeFile ← symsFile ← configFile ← NewFile[configName, Read, DefaultVersion];
    bcdseg ← NewFileSegment[configFile, 1, 1, Read];
    SwapIn[bcdseg];
    bcd ← FileSegmentAddress[bcdseg];
    IF (pages ← bcd.nPages) # 1 THEN
      BEGIN
      Unlock[bcdseg];
      MoveFileSegment[bcdseg, 1, pages];
      SwapIn[bcdseg];
      bcd ← FileSegmentAddress[bcdseg];
      END;
    BEGIN
    ENABLE UNWIND => BEGIN Unlock[bcdseg]; DeleteFileSegment[bcdseg] END;
    IF bcd.versionIdent # BcdDefs.VersionID THEN SIGNAL IncorrectVersion;
    version ← bcd.version;
    creator ← bcd.creator;
    source ← bcd.sourceVersion;
    sgb ← LOOPHOLE[bcd + bcd.sgOffset];
    mtb ← LOOPHOLE[bcd + bcd.mtOffset];
    ssb ← LOOPHOLE[bcd + bcd.ssOffset];
    ftb ← LOOPHOLE[bcd + bcd.ftOffset];
    ss2.base ← @ssb.string;
    mti ← BcdOps.ProcessModules[bcd, CheckModule].mti;
    IF mti = MTNull THEN SIGNAL NoCode
    ELSE
      BEGIN
      mh ← @mtb[mti];
      cfi ← sgb[mh.code.sgi].file;
      IF cfi # FTSelf THEN
	BEGIN
	fileVersion: BcdDefs.VersionStamp;
	cfilebase: POINTER TO BcdDefs.BCD;
	ss2.offset ← ftb[cfi].name;
	ss2.length ← ssb.size[ftb[cfi].name];
	String.AppendSubString[codeFileName, @ss2];
	FOR i: CARDINAL IN [0..codeFileName.length) DO
	  IF codeFileName[i] = '. THEN EXIT;
	  REPEAT FINISHED => String.AppendString[codeFileName, ".bcd"L];
	  ENDLOOP;
	codeFile ← NewFile[codeFileName, SegmentDefs.Read];
	code ← NewFileSegment[codeFile, 1, 1, SegmentDefs.Read];
	SwapIn[code];
	cfilebase ← FileSegmentAddress[code];
	fileVersion ← cfilebase.version;
	Unlock[code];
	IF fileVersion # ftb[cfi].version THEN
	  SIGNAL NoSymbols[ ! UNWIND => DeleteFileSegment[code]];
	MoveFileSegment[code, sgb[mh.code.sgi].base, sgb[mh.code.sgi].pages];
	END
      ELSE
	code ← NewFileSegment[
	  codeFile, sgb[mh.code.sgi].base, sgb[mh.code.sgi].pages,
	  SegmentDefs.Read];
      code.class ← code;
      END;
    IF mti = MTNull OR sgb[mh.sseg].pages = 0 THEN SIGNAL NoSymbols
    ELSE
      BEGIN
      sfi ← sgb[mh.sseg].file;
      IF sfi # FTSelf THEN
	BEGIN
	ss2.offset ← ftb[sfi].name;
	ss2.length ← ssb.size[ftb[sfi].name];
	String.AppendSubString[symsFileName, @ss2];
	FOR i: CARDINAL IN [0..symsFileName.length) DO
	  IF symsFileName[i] = '. THEN EXIT;
	  REPEAT FINISHED => String.AppendString[symsFileName, ".bcd"L];
	  ENDLOOP;
	symsFile ← NewFile[symsFileName, SegmentDefs.Read];
	END;
      IF sgb[mh.sseg].extraPages = 0 THEN SIGNAL NoFGT;
      IF sfi # FTSelf THEN
	BEGIN
	sfilebase: POINTER TO BcdDefs.BCD;
	fileVersion: BcdDefs.VersionStamp;
	symbols ← NewFileSegment[symsFile, 1, 1, SegmentDefs.Read];
	SwapIn[symbols];
	sfilebase ← FileSegmentAddress[symbols];
	fileVersion ← sfilebase.version;
	Unlock[symbols];
	IF fileVersion # ftb[sfi].version THEN
	  SIGNAL NoSymbols[ ! UNWIND => DeleteFileSegment[symbols]];
	MoveFileSegment[
	  symbols, sgb[mh.sseg].base,
	  sgb[mh.sseg].pages + sgb[mh.sseg].extraPages];
	END
      ELSE
	symbols ← NewFileSegment[
	  symsFile, sgb[mh.sseg].base,
	  sgb[mh.sseg].pages + sgb[mh.sseg].extraPages, Read];
      END;
    END;
    IF code # NIL THEN
      BEGIN
      p: POINTER TO ControlDefs.CSegPrefix;
      SwapIn[code];
      p ← FileSegmentAddress[code];
      Dstar ← ~p.header.info.altoCode;
      Unlock[code];
      END;
    Unlock[bcdseg];
    IF saveBcdSeg THEN RETURN;
    DeleteFileSegment[bcdseg];
    bcdseg ← NIL;
    RETURN
    END;
    
  Load: PROCEDURE [name: STRING, saveBcdSeg: BOOLEAN ← FALSE]
    RETURNS [code, symbols, bcdseg: SegmentDefs.FileSegmentHandle] =
    BEGIN OPEN SegmentDefs;
    bcd: POINTER TO BcdDefs.BCD;
    sgb: BcdDefs.Base;
    pages: AltoDefs.PageCount;
    codefile: FileHandle;
    mh: BcdOps.MTHandle;
    code ← symbols ← NIL;
    Dstar ← FALSE;
    filename ← name;
    codefile ← NewFile[name, Read, DefaultVersion];
    bcdseg ← NewFileSegment[codefile, 1, 1, Read];
    SwapIn[bcdseg];
    bcd ← FileSegmentAddress[bcdseg];
    IF (pages ← bcd.nPages) # 1 THEN
      BEGIN
      Unlock[bcdseg];
      MoveFileSegment[bcdseg, 1, pages];
      SwapIn[bcdseg];
      bcd ← FileSegmentAddress[bcdseg];
      END;
    BEGIN
    ENABLE UNWIND => BEGIN Unlock[bcdseg]; DeleteFileSegment[bcdseg] END;
    IF bcd.versionIdent # BcdDefs.VersionID THEN SIGNAL IncorrectVersion;
    version ← bcd.version;
    creator ← bcd.creator;
    source ← bcd.sourceVersion;
    mh ← @LOOPHOLE[bcd + bcd.mtOffset, BcdDefs.Base][FIRST[BcdDefs.MTIndex]];
    sgb ← LOOPHOLE[bcd + bcd.sgOffset];
    IF bcd.nModules # 1 THEN SIGNAL MultipleModules;
    IF bcd.definitions THEN SIGNAL NoCode
    ELSE
      BEGIN
      code ← NewFileSegment[
	codefile, sgb[mh.code.sgi].base, sgb[mh.code.sgi].pages, Read];
      code.class ← code;
      END;
    IF sgb[mh.sseg].pages = 0 THEN SIGNAL NoSymbols
    ELSE
      BEGIN
      IF sgb[mh.sseg].extraPages = 0 THEN SIGNAL NoFGT;
      symbols ← NewFileSegment[
	codefile, sgb[mh.sseg].base, sgb[mh.sseg].pages + sgb[mh.sseg].extraPages,
	Read];
      END;
    END;
    IF code # NIL THEN
      BEGIN
      p: POINTER TO ControlDefs.CSegPrefix;
      SwapIn[code];
      p ← FileSegmentAddress[code];
      Dstar ← ~p.header.info.altoCode;
      Unlock[code];
      END;
    Unlock[bcdseg];
    IF saveBcdSeg THEN RETURN;
    DeleteFileSegment[bcdseg];
    bcdseg ← NIL;
    RETURN
    END;
    
  WriteOneVersion: PROCEDURE [
    version: POINTER TO BcdDefs.VersionStamp, tag: STRING] =
    BEGIN OPEN OutputDefs;
    IF version = NIL THEN RETURN;
    PutString[tag];
    PutTime[LOOPHOLE[version.time]];
    PutString["  on "L];
    PrintMachine[version↑];
    PutCR[];
    END;
    
  WriteVersions: PROCEDURE [
    version, creator, source: POINTER TO BcdDefs.VersionStamp ← NIL] =
    BEGIN
    WriteOneVersion[version, " created "L];
    WriteOneVersion[creator, "    creator "L];
    WriteOneVersion[source, "    source "L];
    OutputDefs.PutCR[];
    RETURN
    END;
    
  PrintMachine: PROCEDURE [stamp: BcdDefs.VersionStamp] =
    BEGIN
    octal: NumberFormat = [8, FALSE, FALSE, 1];
    PutNumber[stamp.net, octal];
    PutChar['#];
    PutNumber[stamp.host, octal];
    PutChar['#];
    RETURN
    END;
    
  WriteFileID: PROCEDURE =
    BEGIN
    PutString[filename];
    IF Dstar THEN PutString[" (/-A)"L];
    Dstar ← FALSE;
    WriteVersions[@version, @creator, @source];
    RETURN
    END;
    
  PrintHti: PROCEDURE [hti: Symbols.HTIndex] =
    BEGIN
    desc: String.SubStringDescriptor;
    s: String.SubString = @desc;
    IF hti = Symbols.HTNull THEN PutString["(anonymous)"]
    ELSE BEGIN symbols.SubStringForHash[s, hti]; PutSubString[s]; END;
    RETURN
    END;
    
  PrintSei: PROCEDURE [sei: Symbols.ISEIndex] =
    BEGIN
    PrintHti[
      IF sei = Symbols.SENull THEN Symbols.HTNull ELSE symbols.seb[sei].hash];
    RETURN
    END;
    
  Indent: PROCEDURE [n: CARDINAL] =
    BEGIN
    PutCR[];
    THROUGH [1..n/8] DO PutChar[IODefs.TAB] ENDLOOP;
    THROUGH [1..n MOD 8] DO PutChar[' ] ENDLOOP;
    RETURN
    END;
    
  PutSubString: PROCEDURE [ss: String.SubString] =
    BEGIN
    i: CARDINAL;
    FOR i IN [ss.offset..ss.offset + ss.length) DO PutChar[ss.base[i]] ENDLOOP;
    RETURN
    END;
    
  herald: STRING ← [50];
  
  LoadLister: PROCEDURE =
    BEGIN OPEN TimeDefs;
    CommanderDefs.InitCommander[herald];
    String.AppendString[to: herald, from: "Alto/Mesa Lister 6.0 of "L];
    AppendDayTime[herald, UnpackDT[ImageDefs.BcdTime[]]];
    herald.length ← herald.length - 3;
    END;
    
  LoadLister[];
  CommanderDefs.WaitCommands[];
  
  END..