-- file FilePack.Mesa
-- last modified by Satterthwaite, June 30, 1980 5:08 PM
-- last modified by Sandman, July 22, 1980 10:05 AM
-- last modified by Bruce, September 10, 1980 6:00 PM
DIRECTORY
BcdDefs USING [
MTIndex, MTRecord, SGIndex, VersionStamp, FTSelf, SGNull, VersionID],
BcdOps USING [BcdBase, NameString],
Copier USING [PurgeMdi],
Strings USING [
SubString, SubStringDescriptor, AppendSubString, EqualSubStrings,
EquivalentSubStrings],
Segments USING [
AddModifyProc, BaseFromSegment, DeleteSegment, EnumerateDirectory, FHandle,
FileFromSegment,
FileProblem, FileNameProblem, FP, InsertFile, LockFile, SegmentAddress, MoveSegment,
NewFile, NewSegment, PagesFromSegment, ReleasableFile, ReleaseFile, SHandle, SwapIn,
SwapOut, Unlock, UnlockFile],
Storage USING [Node, String, Free, FreeString],
String USING [AppendChar, AppendString],
SymbolTable USING [
Base, Handle, NullHandle,
Acquire, CacheSize, Release, SegmentForTable, SetCacheSize, TableForSegment],
Symbols USING [mdType,
HTIndex, MDRecord, MDIndex, FileIndex,
HTNull, CTXNull, IncludedCTXNull, OwnMdi, MDNull, NullFileIndex],
SymbolOps USING [EnterString, SubStringForHash],
SymbolPack USING [mdLimit, stHandle],
SymbolSegment USING [VersionID],
Table USING [Base, Notifier, AddNotify, Allocate, Bounds, DropNotify];
FilePack: PROGRAM
IMPORTS
Copier, Strings, Segments, Storage, String,
SymbolTable, SymbolOps, Table,
own: SymbolPack
EXPORTS Copier =
BEGIN
OPEN Symbols;
SubStringDescriptor: TYPE = Strings.SubStringDescriptor;
-- tables defining the current symbol table
mdb: Table.Base; -- module directory base
FilePackNotify: Table.Notifier = {mdb ← base[mdType]};
-- included module accounting
VersionStamp: TYPE = BcdDefs.VersionStamp;
FileProblem: PUBLIC SIGNAL [HTIndex] RETURNS [BOOLEAN] = CODE;
FileVersion: PUBLIC SIGNAL [HTIndex] RETURNS [BOOLEAN] = CODE;
FileVersionMix: PUBLIC SIGNAL [HTIndex] = CODE;
AnyVersion: VersionStamp = [net:0, host:0, time:0];
EqStamps: PROC [v1, v2: LONG POINTER TO VersionStamp] RETURNS [BOOLEAN] = {
RETURN [v1.time = v2.time AND v1.net = v2.net AND v1.host = v2.host]};
EnterFile: PUBLIC PROC [id: HTIndex, name: Strings.SubString] RETURNS [HTIndex] = {
mdi: MDIndex = FindMdEntry[id, AnyVersion, NormalizeFileName[name]];
RETURN [mdb[mdi].moduleId]};
NormalizeFileName: PROC [name: Strings.SubString] RETURNS [hti: HTIndex] = {
char: CHARACTER;
dot: BOOLEAN ← FALSE;
s: STRING ← Storage.String[name.length+(".bcd"L).length+1];
desc: SubStringDescriptor;
FOR i: CARDINAL IN [name.offset .. name.offset+name.length)
DO
SELECT (char ← name.base[i]) FROM
IN ['A..'Z] => char ← char + ('a-'A);
'. => dot ← TRUE;
ENDCASE;
String.AppendChar[s, char];
ENDLOOP;
IF ~dot THEN String.AppendString[s, ".bcd"L];
IF s[s.length-1] # '. THEN String.AppendChar[s, '.];
desc ← [base:s, offset:0, length: s.length];
hti ← SymbolOps.EnterString[@desc];
Storage.FreeString[s]; RETURN};
HtiToMdi: PUBLIC PROC [hti: HTIndex] RETURNS [mdi: MDIndex] = {
limit: MDIndex = LOOPHOLE[Table.Bounds[mdType].size];
FOR mdi ← FIRST[MDIndex], mdi + SIZE[MDRecord] UNTIL mdi = limit
DO IF hti = mdb[mdi].moduleId THEN RETURN ENDLOOP;
RETURN [MDNull]};
FindMdEntry: PUBLIC PROC [id: HTIndex, version: VersionStamp, file: HTIndex]
RETURNS [mdi: MDIndex] = {
limit: MDIndex = LOOPHOLE[Table.Bounds[mdType].size];
duplicate: BOOLEAN ← FALSE;
FOR mdi ← FIRST[MDIndex], mdi + SIZE[MDRecord] UNTIL mdi = limit
DO
IF mdb[mdi].moduleId = id
THEN {
IF EqStamps[@mdb[mdi].stamp, @version] THEN RETURN;
IF mdb[mdi].stamp = AnyVersion
THEN {
OpenSymbols[mdi ! FileProblem => RESUME [FALSE]];
IF EqStamps[@mdb[mdi].stamp, @version] THEN RETURN};
IF mdb[mdi].stamp # AnyVersion THEN duplicate ← TRUE};
ENDLOOP;
IF duplicate THEN SIGNAL FileVersionMix[id];
mdi ← Table.Allocate[mdType, SIZE[MDRecord]];
mdb[mdi] ← MDRecord[
stamp: version,
moduleId: id,
fileId: file,
ctx: IncludedCTXNull,
shared: FALSE, exported: FALSE,
defaultImport: CTXNull,
file: NullFileIndex];
own.mdLimit ← own.mdLimit + SIZE[MDRecord];
RETURN};
EnterFileSegment: PUBLIC PROC [
id: HTIndex, version: VersionStamp, fileSeg: Segments.SHandle]
RETURNS [mdi: MDIndex] = {
file: Segments.FHandle = Segments.FileFromSegment[fileSeg];
i: FileIndex;
limit: MDIndex = LOOPHOLE[Table.Bounds[mdType].size];
FOR mdi ← FIRST[MDIndex], mdi + SIZE[MDRecord] UNTIL mdi = limit
DO
IF EqStamps[@mdb[mdi].stamp, @version]
AND (id = HTNull OR id = mdb[mdi].moduleId)
THEN GO TO Found;
REPEAT
Found => NULL;
FINISHED => {
mdi ← Table.Allocate[mdType, SIZE[MDRecord]];
mdb[mdi] ← MDRecord[
stamp: version,
moduleId: id,
fileId: HTNull,
ctx: IncludedCTXNull,
shared: FALSE, exported: FALSE,
defaultImport: CTXNull,
file: ];
own.mdLimit ← own.mdLimit + SIZE[MDRecord]};
ENDLOOP;
FOR i IN [0..lastFile]
DO
IF fileTable[i].file = file
THEN {
tableSeg: Segments.SHandle = SymbolTable.SegmentForTable[fileTable[i].table];
IF Segments.BaseFromSegment[tableSeg] = Segments.BaseFromSegment[fileSeg] AND
Segments.PagesFromSegment[tableSeg] = Segments.PagesFromSegment[fileSeg]
THEN GO TO Found};
REPEAT
Found => NULL;
FINISHED => {
i ← lastFile ← lastFile + 1;
UNTIL lastFile < LENGTH[fileTable] DO ExpandFileTable[] ENDLOOP;
fileTable[i] ← [file: file, table: SymbolTable.TableForSegment[fileSeg]];
Segments.LockFile[file]};
ENDLOOP;
mdb[mdi].file ← i; RETURN};
GetSymbolTable: PUBLIC PROC [mdi: MDIndex] RETURNS [base: SymbolTable.Base] = {
index: FileIndex;
OpenSymbols[mdi];
index ← mdb[mdi].file;
IF fileTable[index].file = NIL
THEN base ← NIL
ELSE {
base ← SymbolTable.Acquire[fileTable[index].table];
IF base.stHandle.versionIdent # SymbolSegment.VersionID
THEN {
SymbolTable.Release[base]; base ← NIL;
IF SIGNAL FileProblem[mdb[mdi].fileId]
THEN {
size: CARDINAL = SymbolTable.CacheSize[];
SymbolTable.SetCacheSize[0]; -- clear cache
Segments.UnlockFile[fileTable[index].file];
Segments.DeleteSegment[SymbolTable.SegmentForTable[fileTable[index].table]];
fileTable[index] ← NullFileRecord;
SymbolTable.SetCacheSize[size]}}};
RETURN};
FreeSymbolTable: PUBLIC PROC [base: SymbolTable.Base] = {SymbolTable.Release[base]};
-- low-level file manipulation
FileRecord: TYPE = RECORD[
file: Segments.FHandle,
table: SymbolTable.Handle];
NullFileRecord: FileRecord = FileRecord[NIL, SymbolTable.NullHandle];
fileTable: DESCRIPTOR FOR ARRAY OF FileRecord;
lastFile: INTEGER;
-- file table management
FileInit: PUBLIC PROC [self: STRING, version: VersionStamp] = {
ss: Strings.SubStringDescriptor ← [base:self, offset:0, length:self.length];
Table.AddNotify[FilePackNotify];
IF FindMdEntry[HTNull, version, NormalizeFileName[@ss]] # Symbols.OwnMdi
THEN ERROR;
fileTable ← NIL; lastFile ← -1};
CreateFileTable: PUBLIC PROC [size: CARDINAL] = {
fileTable ← DESCRIPTOR [Storage.Node[size*SIZE[FileRecord]], size];
FOR i: FileIndex IN [0..size) DO fileTable[i] ← NullFileRecord ENDLOOP;
lastFile ← -1};
ExpandFileTable: PROC = {
table: DESCRIPTOR FOR ARRAY OF FileRecord;
i: FileIndex;
size: CARDINAL = LENGTH[fileTable] + 2;
table ← DESCRIPTOR [Storage.Node[size*SIZE[FileRecord]], size];
FOR i IN [0..LENGTH[fileTable]) DO table[i] ← fileTable[i] ENDLOOP;
FOR i IN [LENGTH[fileTable]..size) DO table[i] ← NullFileRecord ENDLOOP;
Storage.Free[BASE[fileTable]];
fileTable ← table};
FileReset: PUBLIC PROC = {
SymbolTable.SetCacheSize[0];
FOR i: INTEGER IN [0..lastFile]
DO
IF fileTable[i].table # SymbolTable.NullHandle
THEN Segments.DeleteSegment[SymbolTable.SegmentForTable[fileTable[i].table]];
IF fileTable[i].file # NIL
THEN {
Segments.UnlockFile[fileTable[i].file];
IF Segments.ReleasableFile[fileTable[i].file]
THEN Segments.ReleaseFile[fileTable[i].file]};
fileTable[i] ← NullFileRecord;
ENDLOOP;
Storage.Free[BASE[fileTable]];
Table.DropNotify[FilePackNotify]};
ModifyFileTable: PROC [name: STRING, file: Segments.FHandle] RETURNS [ok: BOOLEAN] = {
mdi: MDIndex;
limit: MDIndex = LOOPHOLE[Table.Bounds[mdType].size];
i: INTEGER;
seg: Segments.SHandle;
ok ← TRUE;
IF file = NIL THEN {
FOR mdi ← OwnMdi, mdi + SIZE[MDRecord] UNTIL mdi = limit DO
IF (i ← mdb[mdi].file) # NullFileIndex AND fileTable[i].file = NIL THEN {
mdb[mdi].file ← NullFileIndex;
seg ← SymbolTable.SegmentForTable[fileTable[i].table];
IF seg # NIL THEN Segments.DeleteSegment[seg]};
ENDLOOP;
RETURN};
FOR i IN [0..lastFile] DO
IF fileTable[i].file # file THEN LOOP;
seg ← SymbolTable.SegmentForTable[fileTable[i].table];
IF seg # NIL THEN Segments.DeleteSegment[seg];
Segments.UnlockFile[fileTable[i].file];
IF (ok ← Segments.ReleasableFile[fileTable[i].file]) THEN
Segments.ReleaseFile[fileTable[i].file];
fileTable[i] ← NullFileRecord;
FOR mdi ← OwnMdi, mdi + SIZE[MDRecord] UNTIL mdi = limit DO
IF mdb[mdi].file # i THEN LOOP;
mdb[mdi].file ← NullFileIndex;
Copier.PurgeMdi[mdi];
ENDLOOP;
ENDLOOP};
-- file setup
OwnFile: PUBLIC SIGNAL [file: Segments.FHandle] = CODE;
LocateTables: PUBLIC PROC [nTables: CARDINAL] = {
n: CARDINAL ← nTables;
limit: MDIndex = LOOPHOLE[Table.Bounds[mdType].size];
CheckFile: PROC [fp: POINTER TO Segments.FP, s: STRING] RETURNS [BOOLEAN] = {
d1: SubStringDescriptor ← [base:s, offset:0, length:s.length];
d2: SubStringDescriptor;
file: Segments.FHandle;
FOR mdi: MDIndex ← FIRST[MDIndex], mdi+SIZE[MDRecord] UNTIL mdi = limit
DO
SymbolOps.SubStringForHash[@d2, mdb[mdi].fileId];
IF Strings.EquivalentSubStrings[@d1, @d2]
THEN {
mdb[mdi].file ← lastFile ← lastFile+1;
fileTable[lastFile] ← FileRecord[
file: (file ← Segments.InsertFile[fp]),
table: SymbolTable.NullHandle];
Segments.LockFile[file];
IF mdi = OwnMdi THEN SIGNAL OwnFile[fileTable[lastFile].file];
n ← n-1};
ENDLOOP;
RETURN [n = 0]};
Segments.EnumerateDirectory[CheckFile]};
FillFile: PROC [mdi: MDIndex] = {
newFile: FileIndex;
desc: SubStringDescriptor;
name: STRING;
SymbolOps.SubStringForHash[@desc, mdb[mdi].fileId];
newFile ← lastFile + 1;
UNTIL newFile < LENGTH[fileTable] DO ExpandFileTable[] ENDLOOP;
fileTable[newFile] ← NullFileRecord;
name ← Storage.String[desc.length];
Strings.AppendSubString[name, @desc];
BEGIN
fileTable[newFile].file ← Segments.NewFile[name
! Segments.FileNameProblem[], Segments.FileProblem[] =>
IF SIGNAL FileProblem[mdb[mdi].moduleId]
THEN CONTINUE
ELSE GO TO noEntry];
IF fileTable[newFile].file # NIL
THEN Segments.LockFile[fileTable[newFile].file];
lastFile ← newFile;
EXITS
noEntry => newFile ← NullFileIndex;
END;
Storage.FreeString[name];
mdb[mdi].file ← newFile};
OpenSymbols: PROC [mdi: MDIndex] = {
index: FileIndex;
symbolSeg, headerSeg: Segments.SHandle ← NIL;
DeleteHeader: PROC = {
IF headerSeg # NIL
THEN {Segments.Unlock[headerSeg]; Segments.DeleteSegment[headerSeg]; headerSeg ← NIL}};
Fail: PROC [voidEntry: BOOLEAN] = {
IF voidEntry
THEN {
IF headerSeg # NIL THEN DeleteHeader[];
IF fileTable[index].file # NIL
THEN {
Segments.UnlockFile[fileTable[index].file];
IF Segments.ReleasableFile[fileTable[index].file]
THEN Segments.ReleaseFile[fileTable[index].file]};
fileTable[index].file ← NIL}
ELSE DeleteHeader[]};
IF mdb[mdi].file = NullFileIndex THEN FillFile[mdi];
index ← mdb[mdi].file;
IF index # NullFileIndex AND fileTable[index].table = SymbolTable.NullHandle
AND fileTable[index].file # NIL
THEN {
ENABLE {
UNWIND => NULL;
ANY => GO TO badFile};
bcd: BcdOps.BcdBase;
bcdPages: CARDINAL;
mtb, ftb, sgb: Table.Base;
mti: BcdDefs.MTIndex;
sSeg: BcdDefs.SGIndex;
nString: BcdOps.NameString;
d1, d2: SubStringDescriptor;
version: VersionStamp;
bcdPages ← 1;
headerSeg ← Segments.NewSegment[fileTable[index].file, 1, bcdPages];
DO
Segments.SwapIn[headerSeg]; bcd ← Segments.SegmentAddress[headerSeg];
IF bcd.versionIdent # BcdDefs.VersionID THEN GO TO badFile;
IF bcdPages = bcd.nPages THEN EXIT;
bcdPages ← bcd.nPages;
Segments.Unlock[headerSeg]; Segments.SwapOut[headerSeg];
Segments.MoveSegment[headerSeg, 1, bcdPages];
ENDLOOP;
IF bcd.nConfigs # 0 THEN GO TO badFile;
SymbolOps.SubStringForHash[@d1, mdb[mdi].moduleId];
nString ← LOOPHOLE[bcd + bcd.ssOffset];
d2.base ← @nString.string;
ftb ← LOOPHOLE[bcd + bcd.ftOffset];
mtb ← LOOPHOLE[bcd + bcd.mtOffset]; mti ← FIRST[BcdDefs.MTIndex];
UNTIL mti = bcd.mtLimit
DO
d2.offset ← mtb[mti].name;
d2.length ← nString.size[mtb[mti].name];
IF Strings.EqualSubStrings[@d1, @d2] THEN EXIT;
mti ← mti + (SIZE[BcdDefs.MTRecord] + mtb[mti].frame.length);
REPEAT
FINISHED =>
IF bcd.nModules = 1 THEN mti ← FIRST[BcdDefs.MTIndex] ELSE GOTO badFile;
ENDLOOP;
ftb ← LOOPHOLE[bcd + bcd.ftOffset];
version ← IF mtb[mti].file = BcdDefs.FTSelf
THEN bcd.version
ELSE ftb[mtb[mti].file].version;
sgb ← LOOPHOLE[bcd + bcd.sgOffset]; sSeg ← mtb[mti].sseg;
IF sSeg = BcdDefs.SGNull
OR sgb[sSeg].pages = 0 OR sgb[sSeg].file # BcdDefs.FTSelf
THEN GO TO badFile;
IF mdb[mdi].stamp # AnyVersion
AND ~EqStamps[@mdb[mdi].stamp, @version] THEN GO TO wrongVersion;
mdb[mdi].stamp ← version;
symbolSeg ← Segments.NewSegment[
Segments.FileFromSegment[headerSeg], sgb[sSeg].base, sgb[sSeg].pages];
fileTable[index].table ← SymbolTable.TableForSegment[symbolSeg];
DeleteHeader[];
EXITS
badFile => {
ENABLE UNWIND => Fail[TRUE];
Fail[SIGNAL FileProblem[mdb[mdi].moduleId]]};
wrongVersion => {
ENABLE UNWIND => Fail[TRUE];
Fail[SIGNAL FileVersion[mdb[mdi].moduleId]]}}};
TableForModule: PUBLIC PROC [mdi: MDIndex] RETURNS [SymbolTable.Handle] = {
RETURN[fileTable[mdb[mdi].file].table]};
Segments.AddModifyProc[ModifyFileTable];
END.