-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- IndirectionNoDisk.mesa, HGM,  9-Apr-83 23:38:33

DIRECTORY
  Ascii USING [CR, NUL],
  CmFile USING [Handle, ReadLineOrToken, TableError, TitleMatch],
  Heap USING [systemZone],
  StringLookUp USING [InTable, noMatch, TableDesc],
  Token USING [GetCharProcType, FreeTokenString, Item, Object],
  
  Indirect USING [],
  Parms USING [Error, GetMaxChars, GetParms];

IndirectionNoDisk: PROGRAM
  IMPORTS CmFile, Heap, StringLookUp, Token, Parms
  EXPORTS Indirect =
  BEGIN


  parmFileName: LONG STRING ← NIL;

  FindOurName: PROCEDURE =
    BEGIN
    cmFile: CmFile.Handle;
    Option: TYPE = MACHINE DEPENDENT{
      parameterFile(0), noMatch(StringLookUp.noMatch)};
    DefinedOption: TYPE = Option [parameterFile..parameterFile];
    CheckType: PROCEDURE [h: CmFile.Handle, table: StringLookUp.TableDesc]
      RETURNS [index: CARDINAL] = NextValue;
    MyNextValue: PROCEDURE [
      h: CmFile.Handle,
      table: LONG DESCRIPTOR FOR ARRAY DefinedOption OF LONG STRING]
      RETURNS [index: Option] = LOOPHOLE[CheckType];
    optionTable: ARRAY DefinedOption OF LONG STRING ← [
      parameterFile: "ParameterFile"L];
    cmFile ← OpenSection["MyName"L];
    IF cmFile = NIL THEN RETURN;
    DO
      option: Option;
      option ← MyNextValue[cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError => RETRY];
      SELECT option FROM
        noMatch => EXIT;
        parameterFile =>
          BEGIN
          [] ← Token.FreeTokenString[parmFileName];
          parmFileName ← Token.Item[cmFile, FALSE];
          END;
        ENDCASE => ERROR;
      ENDLOOP;
    Close[cmFile];
    END;

  GetParmFileName: PUBLIC PROCEDURE RETURNS [LONG STRING] =
    BEGIN RETURN[parmFileName]; END;

  OpenSection: PUBLIC PROCEDURE [section: LONG STRING] RETURNS [cmFile: CmFile.Handle] =
    BEGIN
    cmFile ← Open[];
    IF cmFile = NIL THEN RETURN;
    IF ~FindSection[cmFile, section] THEN
      BEGIN
      Close[cmFile];
      cmFile ← NIL;
      END;
    END;

  Open: PUBLIC PROCEDURE RETURNS [cmFile: CmFile.Handle] =
    BEGIN
    s: LONG STRING ← Heap.systemZone.NEW[StringBody[Parms.GetMaxChars[]]];
    repH: Handle;
    [] ← Parms.GetParms[s ! Parms.Error => CONTINUE];
    IF s.length = 0 THEN
      BEGIN
      Heap.systemZone.FREE[@s];
      RETURN[NIL];
      END;
    repH ← Heap.systemZone.NEW[Object ←
      [tokenData: [getChar: GetChar], s: s, index: 0]];
    cmFile ← RepToAbs[repH];
    END;

  NextValue: PUBLIC PROCEDURE [h: CmFile.Handle, table: StringLookUp.TableDesc]
    RETURNS [index: CARDINAL] =
    BEGIN
    localLine: STRING ← [lineSize];
    DO
      GobbleRestOfLine[h];  -- consume any parts of preceding line left over by client
      ReadName[h, localLine];
      IF h.break # nameBreak THEN RETURN[StringLookUp.noMatch];
      index ← StringLookUp.InTable[localLine, table, TRUE, TRUE].index;
      IF index < LENGTH[table] THEN EXIT;
      SIGNAL CmFile.TableError[h, localLine];
      ENDLOOP;
    END;

  Close: PUBLIC PROCEDURE [cmFile: CmFile.Handle] =
    BEGIN
    repH: Handle ← Validate[cmFile];
    Heap.systemZone.FREE[@repH.s];
    repH.tokenData.getChar ← NIL;  -- make sure deallocated object will not look valid
    Heap.systemZone.FREE[@cmFile];
    END;

  Handle: TYPE = LONG POINTER TO Object;
  Object: TYPE = MACHINE DEPENDENT RECORD [
    tokenData: Token.Object,
    s: LONG STRING,
    index: CARDINAL];

  AbsToRep: PROCEDURE [h: CmFile.Handle] RETURNS [Handle] = {RETURN[LOOPHOLE[h]]};
  RepToAbs: PROCEDURE [h: Handle] RETURNS [CmFile.Handle] = {
    RETURN[@h.tokenData]};
  Validate: PROCEDURE [h: CmFile.Handle] RETURNS [repH: Handle] =
    BEGIN
    repH ← AbsToRep[h];
    IF repH.tokenData.getChar # GetChar THEN ERROR;
    END;

  GetChar: PRIVATE Token.GetCharProcType =
    BEGIN
    repH: Handle = AbsToRep[h];
    IF repH.index = repH.s.maxlength THEN c ← Ascii.NUL
    ELSE
      BEGIN
      c ← repH.s[repH.index];
      repH.index ← repH.index + 1;
      END;
    END;


  FindSection: PUBLIC PROCEDURE [h: CmFile.Handle, title: LONG STRING]
    RETURNS [opened: BOOLEAN] =
    BEGIN
    localLine: STRING ← [lineSize];
    repH: Handle = Validate[h];
     DO
      ReadLine[h, localLine];
      IF h.break = Ascii.NUL THEN EXIT;
      IF localLine.length # 0 AND localLine[0] = beginSectionName THEN
        BEGIN
        IF CmFile.TitleMatch[localLine, title] THEN RETURN[TRUE];
        END;
      ENDLOOP;
    RETURN[FALSE];
    END;

  ReadLine: PROCEDURE [h: CmFile.Handle, buffer: LONG STRING] =
    -- Suppresses comment lines
    BEGIN
    DO
      CmFile.ReadLineOrToken[h, buffer, Ascii.CR];
      IF h.break # Ascii.CR OR ~IsCommentLine[buffer] THEN EXIT;
      ENDLOOP;
    END;

  lineSize: CARDINAL = 250;
  beginSectionName: CHARACTER = '[;
  commentChar: CHARACTER = '-;
  nameBreak: CHARACTER = ':;
  
  ReadName: PROCEDURE [h: CmFile.Handle, buffer: LONG STRING] =
    -- Suppresses comment lines and any line that is neither a SectionName
    --  nor a SectionLine 
    -- Output assertion: h.break = Ascii.NUL OR h.break = nameBreak OR
    --   h.getChar is positioned just after Ascii.CR
    BEGIN
    DO
      CmFile.ReadLineOrToken[h, buffer, nameBreak];
      IF h.break = Ascii.NUL THEN EXIT;
      IF buffer.length # 0 AND ~IsCommentLine[buffer] THEN
        IF buffer[0] = beginSectionName THEN {
          GobbleRestOfLine[h]; h.break ← Ascii.NUL; EXIT}
        ELSE IF h.break = nameBreak THEN EXIT;
      GobbleRestOfLine[h];
      ENDLOOP;
    END;

  GobbleRestOfLine: PROCEDURE [h: CmFile.Handle] =
    BEGIN
    WHILE SELECT h.break FROM Ascii.CR, Ascii.NUL => FALSE, ENDCASE => TRUE DO
      h.break ← h.getChar[h]; ENDLOOP;
    END;

  IsCommentLine: PROCEDURE [s: LONG STRING] RETURNS [BOOLEAN] =
    BEGIN RETURN[s.length > 1 AND s[0] = commentChar AND s[1] = commentChar]; END;

  -- Initialization
  FindOurName[];
  END.