-- 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.