-- file FilePack.Mesa
-- last modified by Satterthwaite, October 19, 1979 10:42 AM
DIRECTORY
AltoFileDefs: FROM "altofiledefs" USING [FP],
BcdDefs: FROM "bcddefs"
USING [
BCD, MTIndex, MTRecord, NameString, SGIndex, VersionStamp,
FTSelf, SGNull, VersionID],
Copier: FROM "copier",
DirectoryDefs: FROM "directorydefs" USING [EnumerateDirectory],
SegmentDefs: FROM "segmentdefs"
USING [
FileHandle, FileSegmentHandle, OldFileOnly, Read,
DeleteFileSegment, FileError, FileNameError, FileSegmentAddress,
InsertFile, MoveFileSegment, NewFile, NewFileSegment, ReleaseFile,
SwapIn, SwapOut, Unlock],
StringDefs: FROM "stringdefs"
USING [
SubStringDescriptor,
AppendChar, AppendString, AppendSubString,
EqualSubStrings, EquivalentSubStrings],
SymbolTable: FROM "symboltable"
USING [
Base, Handle, NullHandle,
Acquire, Release, SegmentForTable, SetCacheSize, TableForSegment],
Symbols: FROM "symbols"
USING [mdType,
HTIndex, MDRecord, MDIndex, FileIndex,
HTNull, CTXNull, IncludedCTXNull, OwnMdi, MDNull, NullFileIndex],
SymbolOps: FROM "symbolops" USING [EnterString, SubStringForHash],
SymbolSegment: FROM "symbolsegment" USING [VersionID],
SystemDefs: FROM "systemdefs"
USING [
AllocateHeapNode, AllocateHeapString, FreeHeapNode, FreeHeapString],
Table: FROM "table"
USING [Base, Notifier, AddNotify, Allocate, Bounds, DropNotify];
FilePack: PROGRAM
IMPORTS
DirectoryDefs, SegmentDefs, StringDefs, SymbolTable, SymbolOps,
SystemDefs, Table
EXPORTS Copier =
BEGIN
OPEN Symbols;
SubStringDescriptor: TYPE = StringDefs.SubStringDescriptor;
-- tables defining the current symbol table
mdb: Table.Base; -- module directory base
FilePackNotify: Table.Notifier =
BEGIN -- called whenever the main symbol table is repacked
mdb ← base[mdType];
END;
-- 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: PROCEDURE [v1, v2: POINTER TO VersionStamp] RETURNS [BOOLEAN] =
BEGIN
RETURN [v1.time = v2.time AND v1.net = v2.net AND v1.host = v2.host]
END;
EnterFile: PUBLIC PROCEDURE [id: HTIndex, name: STRING] RETURNS [HTIndex] =
BEGIN
mdi: MDIndex = FindMdEntry[id, AnyVersion, NormalizeFileName[name]];
RETURN [mdb[mdi].moduleId]
END;
NormalizeFileName: PROCEDURE [name: STRING] RETURNS [hti: HTIndex] =
BEGIN
i: CARDINAL;
char: CHARACTER;
dot: BOOLEAN ← FALSE;
s: STRING ← SystemDefs.AllocateHeapString[name.length+(".bcd"L).length+1];
desc: SubStringDescriptor;
FOR i IN [0 .. name.length)
DO
SELECT (char ← name[i]) FROM
IN ['A..'Z] => char ← char + ('a-'A);
'. => dot ← TRUE;
ENDCASE;
StringDefs.AppendChar[s, char];
ENDLOOP;
IF ~dot THEN StringDefs.AppendString[s, ".bcd"L];
IF s[s.length-1] # '. THEN StringDefs.AppendChar[s, '.];
desc ← [base:s, offset:0, length: s.length];
hti ← SymbolOps.EnterString[@desc];
SystemDefs.FreeHeapString[s]; RETURN
END;
HtiToMdi: PUBLIC PROCEDURE [hti: HTIndex] RETURNS [mdi: MDIndex] =
BEGIN
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]
END;
FindMdEntry: PUBLIC PROCEDURE [
id: HTIndex, version: VersionStamp, file: HTIndex]
RETURNS [mdi: MDIndex] =
BEGIN
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
BEGIN
IF EqStamps[@mdb[mdi].stamp, @version] THEN RETURN;
IF mdb[mdi].stamp = AnyVersion
THEN
BEGIN
OpenSymbols[mdi !FileProblem => RESUME [FALSE]];
IF EqStamps[@mdb[mdi].stamp, @version] THEN RETURN;
END;
IF mdb[mdi].stamp # AnyVersion THEN duplicate ← TRUE;
END;
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];
RETURN
END;
GetSymbolTable: PUBLIC PROCEDURE [mdi: MDIndex] RETURNS [base: SymbolTable.Base] =
BEGIN
index: FileIndex;
OpenSymbols[mdi];
index ← mdb[mdi].file;
IF fileTable[index].file = NIL
THEN base ← NIL
ELSE
BEGIN
base ← SymbolTable.Acquire[fileTable[index].table];
IF base.stHandle.versionIdent # SymbolSegment.VersionID
THEN
BEGIN
SymbolTable.Release[base]; base ← NIL;
IF SIGNAL FileProblem[mdb[mdi].fileId]
THEN
BEGIN
SegmentDefs.DeleteFileSegment[
SymbolTable.SegmentForTable[fileTable[index].table]];
fileTable[index] ← NullFileRecord;
END;
END;
END;
RETURN
END;
FreeSymbolTable: PUBLIC PROCEDURE [base: SymbolTable.Base] =
BEGIN SymbolTable.Release[base] END;
-- low-level file manipulation
FileRecord: TYPE = RECORD[
file: SegmentDefs.FileHandle,
table: SymbolTable.Handle];
NullFileRecord: FileRecord = FileRecord[NIL, SymbolTable.NullHandle];
fileTable: DESCRIPTOR FOR ARRAY OF FileRecord;
lastFile: INTEGER;
-- file table management
FileInit: PUBLIC PROCEDURE [self: STRING, version: VersionStamp] =
BEGIN
Table.AddNotify[FilePackNotify];
IF FindMdEntry[HTNull, version, NormalizeFileName[self]] # Symbols.OwnMdi
THEN ERROR;
END;
CreateFileTable: PUBLIC PROCEDURE [size: CARDINAL] =
BEGIN OPEN SystemDefs;
i: CARDINAL;
fileTable ← DESCRIPTOR [AllocateHeapNode[size*SIZE[FileRecord]], size];
FOR i IN [0..size) DO fileTable[i] ← NullFileRecord ENDLOOP;
lastFile ← -1;
END;
ExpandFileTable: PROCEDURE =
BEGIN OPEN SystemDefs;
table: DESCRIPTOR FOR ARRAY OF FileRecord;
i: CARDINAL;
size: CARDINAL = LENGTH[fileTable] + 2;
table ← DESCRIPTOR [AllocateHeapNode[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;
FreeHeapNode[BASE[fileTable]];
fileTable ← table;
END;
FileReset: PUBLIC PROCEDURE =
BEGIN
i: INTEGER;
SymbolTable.SetCacheSize[0];
FOR i IN [0..lastFile]
DO
SELECT TRUE FROM
(fileTable[i].table # SymbolTable.NullHandle) =>
SegmentDefs.DeleteFileSegment[
SymbolTable.SegmentForTable[fileTable[i].table]];
(fileTable[i].file # NIL AND fileTable[i].file.lock = 0) =>
SegmentDefs.ReleaseFile[fileTable[i].file];
ENDCASE => NULL;
ENDLOOP;
SystemDefs.FreeHeapNode[BASE[fileTable]];
Table.DropNotify[FilePackNotify];
END;
-- file setup
OwnFile: PUBLIC SIGNAL [file: SegmentDefs.FileHandle] = CODE;
LocateTables: PUBLIC PROCEDURE [nTables: CARDINAL] =
BEGIN
n: CARDINAL ← nTables;
limit: MDIndex = LOOPHOLE[Table.Bounds[mdType].size];
CheckFile: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, s: STRING] RETURNS [BOOLEAN] =
BEGIN
d1: SubStringDescriptor ← [base:s, offset:0, length:s.length];
d2: SubStringDescriptor;
mdi: MDIndex;
FOR mdi ← FIRST[MDIndex], mdi+SIZE[MDRecord] UNTIL mdi = limit
DO
SymbolOps.SubStringForHash[@d2, mdb[mdi].fileId];
IF StringDefs.EquivalentSubStrings[@d1, @d2]
THEN
BEGIN
mdb[mdi].file ← lastFile ← lastFile+1;
fileTable[lastFile] ← FileRecord[
file: SegmentDefs.InsertFile[fp, SegmentDefs.Read],
table: SymbolTable.NullHandle];
IF mdi = OwnMdi THEN SIGNAL OwnFile[fileTable[lastFile].file];
n ← n-1;
END;
ENDLOOP;
RETURN [n = 0]
END;
DirectoryDefs.EnumerateDirectory[CheckFile];
END;
MakeFileTableEntry: PUBLIC PROCEDURE
[file: SegmentDefs.FileHandle, table: SymbolTable.Handle]
RETURNS [FileIndex] =
BEGIN
lastFile ← lastFile + 1;
UNTIL lastFile < LENGTH[fileTable] DO ExpandFileTable[] ENDLOOP;
fileTable[lastFile] ← FileRecord[file: file, table: table];
RETURN [lastFile]
END;
FillFile: PROCEDURE [mdi: MDIndex] =
BEGIN
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 ← SystemDefs.AllocateHeapString[desc.length];
StringDefs.AppendSubString[name, @desc];
BEGIN OPEN SegmentDefs;
fileTable[newFile].file ← NewFile[name, Read, OldFileOnly
! FileNameError, FileError =>
IF SIGNAL FileProblem[mdb[mdi].moduleId]
THEN CONTINUE
ELSE GO TO noEntry];
lastFile ← newFile;
EXITS
noEntry => newFile ← NullFileIndex;
END;
SystemDefs.FreeHeapString[name];
mdb[mdi].file ← newFile;
END;
OpenSymbols: PROCEDURE [mdi: MDIndex] =
BEGIN
index: FileIndex;
symbolSeg, headerSeg: SegmentDefs.FileSegmentHandle ← NIL;
DeleteHeader: PROCEDURE =
BEGIN
IF headerSeg # NIL
THEN
BEGIN OPEN SegmentDefs;
Unlock[headerSeg]; DeleteFileSegment[headerSeg]; headerSeg ← NIL;
END;
END;
Fail: PROCEDURE [voidEntry: BOOLEAN] =
BEGIN
IF voidEntry
THEN
BEGIN
SELECT TRUE FROM
(headerSeg # NIL) => DeleteHeader[];
(fileTable[index].file.lock = 0) =>
SegmentDefs.ReleaseFile[fileTable[index].file];
ENDCASE => NULL;
fileTable[index].file ← NIL;
END
ELSE DeleteHeader[];
END;
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
BEGIN OPEN SegmentDefs;
ENABLE
BEGIN
UNWIND => NULL;
ANY => GO TO badFile
END;
bcd: POINTER TO BcdDefs.BCD;
bcdPages: CARDINAL;
mtb, ftb, sgb: Table.Base;
mti: BcdDefs.MTIndex;
sSeg: BcdDefs.SGIndex;
nString: BcdDefs.NameString;
d1, d2: SubStringDescriptor;
version: VersionStamp;
bcdPages ← 1;
headerSeg ← NewFileSegment[fileTable[index].file, 1, bcdPages, Read];
DO
SwapIn[headerSeg]; bcd ← FileSegmentAddress[headerSeg];
IF bcdPages = bcd.nPages THEN EXIT;
bcdPages ← bcd.nPages;
Unlock[headerSeg]; SwapOut[headerSeg];
MoveFileSegment[headerSeg, 1, bcdPages];
ENDLOOP;
IF bcd.versionident # BcdDefs.VersionID OR bcd.nConfigs > 1
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 StringDefs.EqualSubStrings[@d1, @d2] THEN EXIT;
mti ← mti + (SIZE[BcdDefs.MTRecord] + mtb[mti].frame.length);
REPEAT
FINISHED => 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 ← NewFileSegment[
headerSeg.file,
sgb[sSeg].base, sgb[sSeg].pages,
Read];
symbolSeg.class ← other;
fileTable[index].table ← SymbolTable.TableForSegment[symbolSeg];
DeleteHeader[];
EXITS
badFile => Fail[SIGNAL FileProblem[mdb[mdi].moduleId]];
wrongVersion => Fail[SIGNAL FileVersion[mdb[mdi].moduleId]];
END;
END;
TableForModule: PUBLIC PROCEDURE [mdi: MDIndex] RETURNS [SymbolTable.Handle] =
BEGIN
RETURN[fileTable[mdb[mdi].file].table]
END;
END.