-- Copyright (C) 1986  by Xerox Corporation. All rights reserved. 
-- CRuntimeC.mesa
-- NFS    27-May-86 11:35:37

DIRECTORY
  BcdDefs USING [CTIndex, MTIndex, NameRecord],
  BcdOps USING [BcdBase, NameString],
  BcdOpsExtras USING [CTBaseFromBcd, MTBaseFromBcd, NameStringFromBcd],
  CRuntime USING [ProgramExited, z],
  CRuntimeInternal USING [
    ConfigHandle, GlobalFrameHandle, lock, MainArgsNeeded, SetConfig],
  CString USING [CString, CStringToLongString],
  Environment USING [bytesPerPage],
  Format USING [LongDecimal, LongOctal, StringProc],
  Heap USING [Delete, Flush, GetAttributes],
  LoadState USING [LockBcdInfo, UnlockBcdInfo],
  MFile USING [
    Acquire, Error, GetAccess, GetFullName, Handle, maxNameLength, Release,
    SetAccess],
  MLoader USING [Error, Handle, Load, Start, VersionMismatch],
  MLoaderImpl USING [Handle],
  MStream USING [Error, GetFile, GetLength, Handle, IsIt, Log, SetLogReadLength],
  PrincOps USING [GlobalFrameHandle],
  SpecialRuntime USING [GlobalFrameFromProgram],
  SpecialCRuntime USING [],
  Stream USING [Delete, Handle, PutChar, SendNow],
  String USING [AppendSubString, FreeString, SubStringDescriptor];

CRuntimeC: MONITOR LOCKS CRuntimeInternal.lock
  IMPORTS
    BcdOpsExtras, CRuntime, CRuntimeInternal, CString, Format, Heap, LoadState,
    MFile, MLoader, MStream, SpecialRuntime, Stream, String
  EXPORTS CRuntime, CRuntimeInternal, SpecialCRuntime
  SHARES MLoaderImpl =
  {
  OPEN CRuntime;

  normalOutcome: INTEGER = 0;

  maxLogPages: LONG CARDINAL = 200;  -- Maximum length of log file.

  heapDelete: BOOLEAN ← TRUE;  -- if false, heaps are flushed instead of deleted.
  
  logging: BOOLEAN ← FALSE; -- flags if cleanup being logged.

  StartProgram: PUBLIC PROCEDURE [
    fileName: CString.CString, argc: CARDINAL,
    argv: LONG POINTER TO CString.CString, stdin, stdout, stderr: Stream.Handle]
    RETURNS [outcome: INTEGER ← normalOutcome] = {
    file: LONG STRING ← CString.CStringToLongString[fileName, z];
    {
    ENABLE MFile.Error => {outcome ← -1; CONTINUE; };
    fh: MFile.Handle;
    lh: MLoader.Handle;
    fh ← MFile.Acquire[name: file, access: readOnly, release: []];
    {
    ENABLE {
      MLoader.Error, MLoader.VersionMismatch => {outcome ← -1; CONTINUE; };
      CRuntime.ProgramExited => {outcome ← status; CONTINUE; };
      CRuntimeInternal.MainArgsNeeded => RESUME [argc, argv];
      };
    lh ← MLoader.Load[fh];
    CRuntimeInternal.SetConfig[GFForLH[lh], stdin, stdout, stderr];
    MLoader.Start[lh];
    };
    MFile.Release[fh];
    };
    z.FREE[@file];
    };

  GFForLH: PROCEDURE [lh: MLoader.Handle] RETURNS [PrincOps.GlobalFrameHandle] =
    INLINE {
    RETURN[
      SpecialRuntime.GlobalFrameFromProgram[
        LOOPHOLE[lh, MLoaderImpl.Handle].program]];
    };

  LogCleanUp: PUBLIC INTERNAL PROCEDURE [cH: CRuntimeInternal.ConfigHandle]
    RETURNS [cleanUpNeeded: BOOLEAN ← TRUE] = {
    <<This proc. must be called even if not logging, to prevent attempting
      to clean up twice. >>
    ENABLE MStream.Error => CONTINUE;
    bcd: BcdOps.BcdBase;
    bcdName: String.SubStringDescriptor;
    names: BcdOps.NameString;
    bcdNameRecord: BcdDefs.NameRecord;
    s: LONG STRING;
    openFiles: BOOLEAN = (cH.openStreams # NIL) OR MStream.IsIt[cH.stdin]
      OR MStream.IsIt[cH.stdout] OR MStream.IsIt[cH.stderr];
    IF cH.heap = NIL AND ~openFiles THEN RETURN[FALSE];
    IF ~logging OR log = NIL THEN RETURN;
    bcd ← LoadState.LockBcdInfo[].bcdInfo[cH.config].base;
    IF MStream.GetLength[log] > maxLogPages * Environment.bytesPerPage THEN {
      Stream.Delete[log]; log ← NIL; GetLog[]; WriteLog["Restarted log"L]; };
    names ← BcdOpsExtras.NameStringFromBcd[bcd];
    bcdNameRecord ←
      IF bcd.nConfigs # 0 THEN BcdOpsExtras.CTBaseFromBcd[bcd][
      BcdDefs.CTIndex.FIRST].name
      ELSE BcdOpsExtras.MTBaseFromBcd[bcd][BcdDefs.MTIndex.FIRST].name;
    bcdName ← [
      base: LOOPHOLE[names], offset: bcdNameRecord,
      length: names.size[bcdNameRecord]];
    s ← z.NEW[StringBody [bcdName.length]];
    String.AppendSubString[to: s, from: @bcdName];
    LoadState.UnlockBcdInfo[];
    WriteLog["\N===================================\NCleaning up after "L];
    WriteLog[s];
    String.FreeString[z, s];
    };

  DeleteAndLogHeap: PUBLIC INTERNAL PROCEDURE [z: UNCOUNTED ZONE] = {
    WriteLog[IF heapDelete THEN "\NDeleting heap "L ELSE "\NFlushing heap "L];
    Format.LongOctal[WriteLog, LOOPHOLE[z]];
    WriteLog["  (size: "L];
    Format.LongDecimal[WriteLog, Heap.GetAttributes[z].heapPages];
    WriteLog[" pages)..."L];
    IF heapDelete THEN Heap.Delete[z: z, checkEmpty: FALSE] ELSE Heap.Flush[z];
    WriteLog["done"L];
    };

  DeleteAndLogStream: PUBLIC INTERNAL PROCEDURE [sH: Stream.Handle] = {
    IF MStream.IsIt[sH] THEN {  -- write in log only if closing an MStream
      ENABLE
        MStream.Error, MFile.Error => {
          WriteLog["\N*** Error closing file"L]; CONTINUE; };
      fileName: LONG STRING ← [MFile.maxNameLength];
      file: MFile.Handle = MStream.GetFile[sH];
      WriteLog["\NClosing "L];
      MFile.GetFullName[file, fileName];
      WriteLog[fileName];
      WriteLog["..."L];
      IF MFile.GetAccess[file] = log THEN {
        -- change access to writeOnly so that notify procs. are called.
        MStream.SetLogReadLength[sH, MStream.GetLength[sH]];
        MFile.SetAccess[file, writeOnly ! MFile.Error => CONTINUE];
        };
      Stream.Delete[sH];
      WriteLog["done"L];
      }
    ELSE Stream.Delete[sH];
    };

  WriteLog: Format.StringProc = {
    ENABLE MStream.Error => CONTINUE;
    IF ~logging OR log = NIL THEN RETURN;
    FOR i: CARDINAL IN [0..s.length) DO Stream.PutChar[log, s.text[i]]; ENDLOOP;
    Stream.SendNow[log];
    MStream.SetLogReadLength[log, MStream.GetLength[log]];
    };

  SetHeapDelete: PUBLIC ENTRY PROCEDURE [deleteHeap: BOOLEAN]
    RETURNS [oldDeleteHeap: BOOLEAN] = {
    oldDeleteHeap ← heapDelete;
    heapDelete ← deleteHeap;
  };

  GetHeapDelete: PUBLIC ENTRY PROCEDURE RETURNS [deleteHeap: BOOLEAN] = {
    deleteHeap ← heapDelete; };
  
  SetLogging: PUBLIC ENTRY PROCEDURE [
    newLogging: BOOLEAN] RETURNS[oldLogging: BOOLEAN] = {
    oldLogging ← logging;
    logging ← newLogging;
    IF newLogging AND log = NIL THEN GetLog[];
  };
   
  GetLogging: PUBLIC ENTRY PROCEDURE RETURNS[isLogging: BOOLEAN] = {
    isLogging ← logging;};

  GetLog: PROCEDURE = {
    log ← MStream.Log["CRuntime.log"L, [] ! MStream.Error => CONTINUE]; };

  log: PUBLIC MStream.Handle ← NIL;

  }.