-- file FileTableImpl.mesa  
-- last edited by Lewis on  6-Jan-82 17:56:02
-- last edited by Satterthwaite, December 27, 1982 9:43 am

DIRECTORY
  BcdDefs USING [BCD, FTIndex, FTRecord, FTSelf, NameRecord, VersionStamp],
  CIFS: TYPE USING [OpenFile, Close, Error, GetFC, Open, read],
  ConvertUnsafe: TYPE USING [ToRope],
  Error USING [ErrorFile, WrongFileVersion],
  FileTable: TYPE USING [],
  PackagerDefs USING [globalData, nullSourceIndex],
  SourceBcd USING [bcdBases, bcdLimits, SubStringForName],
  Space: TYPE USING [
    Handle, nullHandle, virtualMemory, CopyIn, Create, Delete, Error, LongPointer, Map],
  String USING [AppendString, AppendSubString, SubString, SubStringDescriptor];

FileTableImpl: PROGRAM
    IMPORTS
      CIFS, ConvertUnsafe, Error, PackagerDefs, SourceBcd, Space, String
    EXPORTS FileTable =
  BEGIN

  FTIndex: TYPE = BcdDefs.FTIndex;
  SubStringDescriptor: TYPE = String.SubStringDescriptor;
  SubString: TYPE = String.SubString;
  

  UnknownFile: PUBLIC ERROR [fti: FTIndex] = CODE;

  maxFiles: NAT = (FTIndex.LAST-FTIndex.FIRST)/BcdDefs.FTRecord.SIZE + 1;
  FileSequence: TYPE = RECORD [SEQUENCE length: [0..maxFiles] OF CIFS.OpenFile];
  BitSequence: TYPE = RECORD [PACKED SEQUENCE length: [0..maxFiles] OF BOOL];
  nullFile: CIFS.OpenFile = NIL;
  
  fileArray: REF FileSequence ← NIL;	-- REF so GC can find OpenFile objects
  fileStampChecked: LONG POINTER TO BitSequence ← NIL;
  

  headerSeg: Space.Handle ← Space.nullHandle;
  header: LONG POINTER TO BcdDefs.BCD ← NIL;


  Build: PUBLIC PROC = {
    OPEN BcdDefs;
    numFiles: NAT;
    fileNameString: STRING ← [100];
    ftiNameDesc: SubStringDescriptor;
    ftiName: SubString ← @ftiNameDesc;
    saveIndex: CARDINAL = PackagerDefs.globalData.textIndex;
    i: CARDINAL;
    PackagerDefs.globalData.textIndex ← PackagerDefs.nullSourceIndex;
    -- allocate file handle array
    numFiles ← IndexForFti[SourceBcd.bcdLimits.ft];
    fileArray ← --PackagerDefs.globalData.zone.--NEW[FileSequence[numFiles]];
    fileStampChecked ← PackagerDefs.globalData.zone.NEW[BitSequence[numFiles]];
    FOR i IN [0..numFiles) DO 
      fileArray[i] ← nullFile;  fileStampChecked[i] ← FALSE; 
      ENDLOOP;
    -- enter file names into symtab (if not already there) to speed file lookup
    i ← 0;
    FOR fti: FTIndex ← FIRST[FTIndex], fti+SIZE[FTRecord]
     UNTIL fti = SourceBcd.bcdLimits.ft DO
      SourceBcd.SubStringForName[ftiName, SourceBcd.bcdBases.ftb[fti].name];
      fileNameString.length ← 0;
      String.AppendSubString[fileNameString, ftiName];
      FOR j: CARDINAL IN [0..fileNameString.length) DO
        IF fileNameString[j] = '. THEN EXIT;
	REPEAT FINISHED => String.AppendString[fileNameString, ".bcd"L];
	ENDLOOP;
      fileArray[i] ← CIFS.Open[ConvertUnsafe.ToRope[fileNameString], CIFS.read
          ! CIFS.Error => TRUSTED {CONTINUE}];
      i ← i + 1;
      ENDLOOP;
    headerSeg ← Space.Create[size: 1, parent: Space.virtualMemory];
    headerSeg.Map[];
    header ← headerSeg.LongPointer[];
    PackagerDefs.globalData.textIndex ← saveIndex};

  IndexForFti: PROC [fti: FTIndex] RETURNS [CARDINAL] = INLINE {
    RETURN[ LOOPHOLE[fti,CARDINAL]/SIZE[BcdDefs.FTRecord] ]};
    
  Destroy: PUBLIC PROC = {
    IF headerSeg # Space.nullHandle THEN {
      Space.Delete[headerSeg]; headerSeg ← Space.nullHandle};
    IF fileArray # NIL THEN {
      FOR i: CARDINAL IN [0..fileArray.length) DO
        IF fileArray[i] # nullFile THEN{
          CIFS.Close[fileArray[i]]; fileArray[i] ← nullFile};
        ENDLOOP;
      --PackagerDefs.globalData.zone.--FREE[@fileArray]}};
      

  HandleForFile: PUBLIC PROC [fti: FTIndex] RETURNS [file: CIFS.OpenFile←nullFile] = {
    --  copies into non-GC storage are ok
    index: CARDINAL;
    IF fti = BcdDefs.FTSelf THEN file ← PackagerDefs.globalData.sourceBcdFile
    ELSE {
      index ← IndexForFti[fti];
      IF index >= fileArray.length OR (file ← fileArray[index]) = nullFile THEN 
        ERROR UnknownFile[fti];
      IF ~fileStampChecked[index] THEN {
          BEGIN
          headerSeg.CopyIn[window: [file: file.GetFC, base: 1] ! Space.Error => {GO TO fail}];
          IF header.version # SourceBcd.bcdBases.ftb[fti].version THEN
            Error.WrongFileVersion[
	      class: error, fti: fti, 
	      requiredVersion: SourceBcd.bcdBases.ftb[fti].version,
	      actualVersion: header.version];
          EXITS
            fail => {
              Error.ErrorFile[class: error, fti: fti, s: "cannot be opened"L];
              ERROR UnknownFile[fti]};
          END; 
        fileStampChecked[index] ← TRUE}};
    RETURN};
    
  END.