
    <<FilePack.mesa>>
        <<Copyright © 1985 by Xerox Corporation.  All rights reserved.>>
        <<Satterthwaite, June 10, 1986 9:58:21 am PDT>>
        <<Paul Rovner, September 6, 1983 11:54 pm>>
        <<Russ Atkinson (RRA) March 6, 1985 10:28:26 pm PST>>

    DIRECTORY
        Alloc USING [Handle, Notifier, AddNotify, DropNotify, Top, Words],
        ConvertUnsafe USING [SubString, SubStringToRope],
        Copier USING [],
        FileParms USING [ActualId, BindingProc, Ops, Name, nullActual, nullName],
        Rope USING [Flatten, Length, ROPE],
        SymbolTable USING [Base, Handle, nullHandle, voidHandle, Acquire, Forget, Locked, Release],
        Symbols USING [Base, CTXNull, FileIndex, IncludedCTXNull, MDIndex, MDFirst, MDNull, MDRecord, mdType, Name, nullFileIndex, 
        nullName, OwnMdi],
        SymbolOps USING [EnterString, SubStringForName],
        SymbolPack USING [mdLimit, stHandle],
        SymbolSegment USING [VersionID],
        TimeStamp USING [Stamp];

    FilePack: PROGRAM
        IMPORTS Alloc, ConvertUnsafe, Rope, SymbolTable, SymbolOps,  own: SymbolPack
        EXPORTS Copier = { 
        OPEN Symbols;

        table: Alloc.Handle _ NIL;

    <<tables defining the current symbol table>>

        mdb: Symbols.Base;        -- module directory base

        FilePackNotify: Alloc.Notifier = {mdb _ base[mdType]};


    <<included module accounting>>

        VersionStamp: TYPE = TimeStamp.Stamp;

        FileProblem: PUBLIC SIGNAL [Name] RETURNS [BOOL] = CODE;
        FileVersion: PUBLIC SIGNAL [Name] RETURNS [BOOL] = CODE;
        FileVersionMix: PUBLIC SIGNAL [Name] = CODE;

        AnyVersion: VersionStamp = [net:0, host:0, time:0];


        EnterFile: PUBLIC PROC [formalId, typeId: Name, defaultFile: Rope.ROPE]
            RETURNS [mdi: MDIndex _ MDNull] = {

            BindItem: FileParms.BindingProc = {
                IF actual # FileParms.nullActual THEN
                    mdi _ FindMdEntry[typeId, actual.version, EnterRope[actual.locator]]
                ELSE [] _ SIGNAL FileProblem[formalId]};    -- need better error message

            fd, td: FileParms.Name;
            fd _ (SymbolOps.SubStringForName[formalId]).SubStringToRope;
            td _ (SymbolOps.SubStringForName[typeId]).SubStringToRope;
            fileParms.Binding[fd, td, defaultFile, BindItem];
            RETURN};

        FindMdEntry: PUBLIC PROC [id: Name, version: VersionStamp, file: Name]
            RETURNS [mdi: MDIndex] = {
            limit: MDIndex = table.Top[mdType];
            duplicate: BOOL _ FALSE;
            FOR mdi _ MDFirst, mdi + MDRecord.SIZE UNTIL mdi = limit DO
                IF mdb[mdi].moduleId = id THEN {
                    IF mdb[mdi].stamp = version THEN RETURN;
                    duplicate _ TRUE};
                ENDLOOP;
            IF duplicate THEN SIGNAL FileVersionMix[id];
            mdi _ table.Words[mdType, MDRecord.SIZE];
            mdb[mdi] _ MDRecord[
                stamp: version,
                moduleId: id,
                fileId: file,
                ctx: IncludedCTXNull,
                shared: FALSE, exported: FALSE,
                defaultImport: CTXNull,
                file: nullFileIndex];
            own.mdLimit _ own.mdLimit + MDRecord.SIZE;
            RETURN};


        GetSymbolTable: PUBLIC PROC [mdi: MDIndex] RETURNS [base: SymbolTable.Base] = {
            index: FileIndex;
            OpenSymbols[mdi];
            index _ mdb[mdi].file;
            IF fileTable[index].file = nullHandle.file THEN base _ NIL
            ELSE {
                base _ SymbolTable.Acquire[fileTable[index]];
                IF base.stHandle.versionIdent # SymbolSegment.VersionID THEN {
                    SymbolTable.Release[base];  base _ NIL;
                    IF SIGNAL FileProblem[mdb[mdi].fileId] THEN GO TO flush}
                ELSE IF base.stHandle.version # mdb[mdi].stamp THEN {
                    SymbolTable.Release[base];  base _ NIL;
                    IF SIGNAL FileProblem[mdb[mdi].fileId] THEN GO TO flush};
                EXITS
                    flush => {
                        SymbolTable.Forget[fileTable[index] ! SymbolTable.Locked => {CONTINUE}];
                        fileParms.Release[fileTable[index]];
                        fileTable[index] _ voidHandle}};
            RETURN};

        FreeSymbolTable: PUBLIC PROC [base: SymbolTable.Base] = {SymbolTable.Release[base]};

        EnterRope: PROC[rope: Rope.ROPE] RETURNS[Symbols.Name] = INLINE {
            ss: ConvertUnsafe.SubString;
            ss.base _ LOOPHOLE[rope.Flatten[]];
            ss.offset _ 0;
            ss.length _ rope.Length[];
            RETURN[SymbolOps.EnterString[ss]]};


    <<low-level file manipulation>>

        FileHandle: TYPE = SymbolTable.Handle;
        FileTable: TYPE = RECORD[SEQUENCE length: NAT OF FileHandle];

        nullHandle: FileHandle = SymbolTable.nullHandle;
        voidHandle: FileHandle = SymbolTable.voidHandle;

        fileTable: REF FileTable;
        lastFile: INTEGER;


        <<file table management>>

        fileParms: FileParms.Ops;

        FileInit: PUBLIC PROC [
            self: FileParms.ActualId,
            ownTable: Alloc.Handle,
            ops: FileParms.Ops] = {
            table _ ownTable;  table.AddNotify[FilePackNotify];
            IF FindMdEntry[nullName, self.version, EnterRope[self.locator]] # Symbols.OwnMdi
                THEN ERROR;
            fileParms _ ops;  fileTable _ NIL; lastFile _ -1};

        CreateFileTable: PUBLIC PROC [size: CARDINAL] = {
            n: CARDINAL = size+1;    -- allow for ownMdi
            fileTable _ NEW[FileTable[n]];
            FOR i: FileIndex IN [0..n) DO fileTable[i] _ nullHandle ENDLOOP;
            lastFile _ -1};

        ExpandFileTable: PROC = {
            newTable: REF FileTable;
            i: FileIndex;
            size: CARDINAL = fileTable.length + 2;
            newTable _ NEW[FileTable[size]];
            FOR i IN [0..fileTable.length) DO newTable[i] _ fileTable[i] ENDLOOP;
            FOR i IN [fileTable.length..size) DO newTable[i] _ nullHandle ENDLOOP;
            fileTable _ newTable};

        FileReset: PUBLIC PROC = {
            FOR i: INTEGER IN [0..lastFile] DO
                IF fileTable[i] # nullHandle THEN fileParms.Release[fileTable[i]];
                fileTable[i] _ nullHandle;
                ENDLOOP;
            table.DropNotify[FilePackNotify]; table _ NIL};


        <<file setup>>

        MdiToFile: PROC [mdi: MDIndex] RETURNS [FileIndex] = {
            IF mdb[mdi].file = nullFileIndex THEN {
                newFile: FileIndex = lastFile + 1;
                UNTIL newFile < fileTable.length DO ExpandFileTable[] ENDLOOP;
                fileTable[newFile] _ nullHandle;
                lastFile _ newFile;
                mdb[mdi].file _ newFile};
            RETURN [mdb[mdi].file]};


        OpenSymbols: PROC [mdi: MDIndex] = {
            index: FileIndex = MdiToFile[mdi];
            IF fileTable[index] = nullHandle THEN {
                d1, d2: FileParms.Name;
                d1 _ (SymbolOps.SubStringForName[mdb[mdi].moduleId]).SubStringToRope;
                d2 _ (SymbolOps.SubStringForName[mdb[mdi].fileId]).SubStringToRope;
                fileTable[index] _ fileParms.Acquire[d1, [mdb[mdi].stamp, d2]];
                IF fileTable[index] = nullHandle
                    AND (SIGNAL FileProblem[mdb[mdi].moduleId]) THEN
                        fileTable[index] _ voidHandle}};


        TableForModule: PUBLIC PROC [mdi: MDIndex] RETURNS [SymbolTable.Handle] = {
            RETURN[fileTable[mdb[mdi].file]]};

        <<mdi bypass>>

            MapSymbols: PUBLIC PROC [id: FileParms.ActualId] RETURNS [base: SymbolTable.Base] = {
                IF id = FileParms.nullActual THEN base _ NIL
                ELSE {
                    handle: SymbolTable.Handle = fileParms.Acquire[FileParms.nullName, id];
                    IF handle.file = nullHandle.file THEN base _ NIL
                    ELSE {
                        base _ SymbolTable.Acquire[handle];
                        IF base.stHandle.versionIdent # SymbolSegment.VersionID THEN {
                            fileParms.Release[handle]; SymbolTable.Release[base]; base _ NIL}}};
                RETURN};

            UnmapSymbols: PUBLIC PROC [SymbolTable.Base] = FreeSymbolTable;

            }.

