-- file ListerUtilities.mesa
-- last edited by Satterthwaite on May 12, 1983 12:30 pm
DIRECTORY
Alloc: TYPE USING [Base, Handle, Notifier, Selector],
BcdDefs: TYPE,
BcdOps: TYPE USING [BcdBase, NameString],
CharIO: TYPE USING [PutChar, PutNumber, PutString, PutSubString],
Environment: TYPE USING [PageCount],
Exec: TYPE USING [w],
File: TYPE USING [Capability, nullCapability],
FileSegment: TYPE USING [Pages, nullPages],
FileStream: TYPE USING [Create],
Format: TYPE USING [NumberFormat],
Heap: TYPE USING [systemZone],
ListerUtil: TYPE USING [],
OSMiscOps: TYPE USING [FileError, FindFile],
Space: TYPE USING [
Handle, nullHandle, virtualMemory, Create, Delete, LongPointer, Map],
Stream: TYPE USING [
DeleteProcedure, Handle, Object, PutByteProcedure, defaultObject],
Strings: TYPE USING [
AppendChar, AppendString, AppendSubString, EquivalentSubStrings,
String, SubString, SubStringDescriptor],
Symbols: TYPE USING [
bodyType, ctxType, Name, nullName, htType, ISEIndex, mdType,
SENull, seType, ssType],
SymbolSegment: TYPE USING [Base, extType, ltType, treeType, Tables],
SymbolTable: TYPE USING [Base],
Time: TYPE USING [Append, Unpack],
TTY: TYPE USING [PutChar];
ListerUtilities: PROGRAM
IMPORTS
CharIO, Exec, FileStream, Heap, OSMiscOps, Space, Stream, Strings, Time, TTY
EXPORTS Alloc, ListerUtil = {
UnknownModule: PUBLIC ERROR = CODE;
version, creator, source: BcdDefs.VersionStamp;
filename: Strings.String;
symbols: SymbolTable.Base;
bases: PRIVATE ARRAY SymbolSegment.Tables OF Alloc.Base;
SetRoutineSymbols: PUBLIC PROC [s: SymbolTable.Base] = {
OPEN s.stHandle;
symbase: SymbolSegment.Base ← LOOPHOLE[s.stHandle];
symbols ← s;
bases[SymbolSegment.treeType] ← symbase + treeBlock.offset;
bases[Symbols.seType] ← symbase + seBlock.offset;
bases[Symbols.htType] ← symbase + htBlock.offset;
bases[Symbols.ssType] ← symbase + ssBlock.offset;
bases[Symbols.ctxType] ← symbase + ctxBlock.offset;
bases[Symbols.mdType] ← symbase + mdBlock.offset;
bases[Symbols.bodyType] ← symbase + bodyBlock.offset;
bases[SymbolSegment.ltType] ← symbase + litBlock.offset;
bases[SymbolSegment.extType] ← symbase + extBlock.offset;
UpdateBases[]};
NotifyLink: TYPE = LONG POINTER TO NotifyNode;
NotifyNode: TYPE = RECORD [notifier: Alloc.Notifier, link: NotifyLink];
notifyList: NotifyLink ← NIL;
AddNotify: PUBLIC PROC [h: Alloc.Handle, proc: Alloc.Notifier] = {
p: NotifyLink =
(Heap.systemZone).NEW[NotifyNode ← [notifier: proc, link: notifyList]];
notifyList ← p;
proc[DESCRIPTOR[bases]]};
DropNotify: PUBLIC PROC [h: Alloc.Handle, proc: Alloc.Notifier] = {
p, q: NotifyLink;
IF notifyList = NIL THEN RETURN;
p ← notifyList;
IF p.notifier = proc THEN notifyList ← p.link
ELSE {
DO
q ← p;
p ← p.link;
IF p = NIL THEN RETURN;
IF p.notifier = proc THEN EXIT
ENDLOOP;
q.link ← p.link};
(Heap.systemZone).FREE[@p]};
UpdateBases: PROC = {
FOR p: NotifyLink ← notifyList, p.link UNTIL p = NIL DO
p.notifier[DESCRIPTOR[bases]] ENDLOOP};
Bounds: PUBLIC PROC [h: Alloc.Handle, table: Alloc.Selector]
RETURNS [base: Alloc.Base, size: CARDINAL] = {
OPEN symbols.stHandle;
RETURN [bases[table], SELECT table FROM
SymbolSegment.treeType => treeBlock.size,
Symbols.seType => seBlock.size,
Symbols.htType => htBlock.size,
Symbols.ssType => ssBlock.size,
Symbols.ctxType => ctxBlock.size,
Symbols.mdType => mdBlock.size,
Symbols.bodyType => bodyBlock.size,
SymbolSegment.ltType => litBlock.size,
SymbolSegment.extType => extBlock.size,
ENDCASE => ERROR]};
SetFileName: PUBLIC PROC [name, root, extension: Strings.String] = {
name.length ← 0;
FOR i: CARDINAL IN [0 .. root.length) DO
IF root[i] = '. THEN EXIT;
Strings.AppendChar[name, root[i]];
ENDLOOP;
IF extension # NIL THEN {
Strings.AppendChar[name, '.]; Strings.AppendString[name, extension]}};
CreateStream: PUBLIC PROC [name: Strings.String] RETURNS [Stream.Handle] = {
RETURN [FileStream.Create[OSMiscOps.FindFile[name, $write]]]};
LoadBcd: PUBLIC PROC [fileId: Strings.String] RETURNS [bcd: FileSegment.Pages] = {
file: File.Capability;
file ← OSMiscOps.FindFile[fileId, $read ! OSMiscOps.FileError => {GO TO noFile}];
filename ← fileId;
bcd ← ReadHeader[file];
RETURN
EXITS
noFile => bcd ← FileSegment.nullPages};
LoadModule: PUBLIC PROC [bcd: FileSegment.Pages, typeId: Strings.String]
RETURNS [mti: BcdDefs.MTIndex, code, symbols: FileSegment.Pages] = {
mti ← BcdDefs.MTNull; code ← symbols ← FileSegment.nullPages;
IF bcd # FileSegment.nullPages THEN {
BcdBase: PROC [p: LONG POINTER] RETURNS [BcdDefs.Base] = INLINE {
RETURN [LOOPHOLE[p, BcdDefs.Base]]};
bcdSpace: Space.Handle = MapPages[bcd];
bcdBase: BcdOps.BcdBase = bcdSpace.LongPointer[];
mtb: BcdDefs.Base = BcdBase[bcdBase + bcdBase.mtOffset];
ftb: BcdDefs.Base = BcdBase[bcdBase + bcdBase.ftOffset];
sgb: BcdDefs.Base = BcdBase[bcdBase + bcdBase.sgOffset];
nString: BcdOps.NameString = LOOPHOLE[bcdBase + bcdBase.ssOffset];
AcquireFile: PROC [fti: BcdDefs.FTIndex] RETURNS [file: File.Capability] = {
IF fti = BcdDefs.FTSelf THEN file ← bcd.file
ELSE {
d: Strings.SubStringDescriptor ← [@nString.string, TRASH, TRASH];
fileName: STRING ← [100];
fileSpace: Space.Handle;
fileBase: BcdOps.BcdBase;
d.offset ← ftb[fti].name; d.length ← nString.size[ftb[fti].name];
Strings.AppendSubString[fileName, @d];
FOR i: CARDINAL IN [0..fileName.length) DO
IF fileName[i] = '. THEN EXIT;
REPEAT
FINISHED => Strings.AppendString[fileName, ".bcd"L];
ENDLOOP;
file ← OSMiscOps.FindFile[fileName, $read
! OSMiscOps.FileError => {GO TO NoFile}];
fileSpace ← MapPages[[file, [base: 1, pages: 1]]];
fileBase ← fileSpace.LongPointer;
IF fileBase.versionIdent # BcdDefs.VersionID
OR fileBase.version # ftb[fti].version THEN {
Space.Delete[fileSpace]; GO TO BadFile};
Space.Delete[fileSpace];
EXITS
NoFile, BadFile => file ← File.nullCapability};
RETURN};
d1: Strings.SubStringDescriptor ← [typeId, 0, typeId.length];
d2: Strings.SubStringDescriptor ← [@nString.string, TRASH, TRASH];
mti ← BcdDefs.MTIndex.FIRST;
UNTIL mti = bcdBase.mtLimit DO
d2.offset ← mtb[mti].name; d2.length ← nString.size[mtb[mti].name];
IF Strings.EquivalentSubStrings[@d1, @d2] THEN EXIT;
mti ← mti + (WITH m: mtb[mti] SELECT FROM
direct => BcdDefs.MTRecord.direct.SIZE + m.length*BcdDefs.Link.SIZE,
indirect => BcdDefs.MTRecord.indirect.SIZE,
multiple => BcdDefs.MTRecord.multiple.SIZE,
ENDCASE => ERROR);
REPEAT
FINISHED =>
IF bcdBase.nModules = 1 THEN mti ← BcdDefs.MTIndex.FIRST
ELSE {DeleteSpace[bcdSpace]; ERROR UnknownModule};
ENDLOOP;
IF ~bcdBase.definitions THEN {
code.file ← AcquireFile[sgb[mtb[mti].code.sgi].file];
IF code.file # File.nullCapability THEN
code.span ← [sgb[mtb[mti].code.sgi].base, sgb[mtb[mti].code.sgi].pages]};
IF sgb[mtb[mti].sseg].pages # 0 THEN {
symbols.file ← AcquireFile[sgb[mtb[mti].sseg].file];
IF symbols.file # File.nullCapability THEN
symbols.span ← [
sgb[mtb[mti].sseg].base,
sgb[mtb[mti].sseg].pages + sgb[mtb[mti].sseg].extraPages]};
DeleteSpace[bcdSpace]};
RETURN};
MapPages: PUBLIC PROC [pages: FileSegment.Pages] RETURNS [s: Space.Handle] = {
IF pages = FileSegment.nullPages THEN s ← Space.nullHandle
ELSE {
s ← Space.Create[size: pages.span.pages, parent: Space.virtualMemory];
s.Map[window: [file: pages.file, base: pages.span.base]]};
RETURN};
DeleteSpace: PUBLIC PROC [s: Space.Handle] = {
IF s # Space.nullHandle THEN Space.Delete[s]};
ReadHeader: PROC [file: File.Capability] RETURNS [
bcdPages: FileSegment.Pages ← FileSegment.nullPages] = {
headerSpace: Space.Handle ← Space.nullHandle;
DeleteHeader: PROC = {
IF headerSpace # Space.nullHandle THEN {
Space.Delete[headerSpace];
headerSpace ← Space.nullHandle}};
IF file # File.nullCapability THEN {
ENABLE {
UNWIND => {NULL};
ANY => {GO TO badFile}};
BcdBase: PROC [p: LONG POINTER] RETURNS [BcdDefs.Base] = INLINE {
RETURN [LOOPHOLE[p, BcdDefs.Base]]};
bcd: BcdOps.BcdBase;
nPages: CARDINAL ← 8;
DO
headerSpace ← Space.Create[size: nPages, parent: Space.virtualMemory];
headerSpace.Map[window: [file: file, base: 1]];
bcd ← headerSpace.LongPointer[];
IF bcd.versionIdent # BcdDefs.VersionID THEN GO TO badFile;
IF nPages >= bcd.nPages THEN EXIT;
nPages ← bcd.nPages;
Space.Delete[headerSpace]; headerSpace ← Space.nullHandle
ENDLOOP;
bcdPages ← [file, [1, bcd.nPages]];
version ← bcd.version;
creator ← bcd.creator;
source ← bcd.sourceVersion;
DeleteHeader[];
EXITS
badFile => {DeleteHeader[]; bcdPages ← FileSegment.nullPages}};
RETURN};
PutVersionId: PUBLIC PROC [out: Stream.Handle, stamp: BcdDefs.VersionStamp] = {
OPEN CharIO;
StampWords: CARDINAL = BcdDefs.VersionStamp.SIZE;
str: PACKED ARRAY [0..4*StampWords) OF [0..16) = LOOPHOLE[stamp];
digit: STRING = "0123456789abcdef"L;
PutChar[out, '"];
FOR i: NAT IN [0..4*StampWords) DO PutChar[out, digit[str[i]]] ENDLOOP;
PutString[out, "\" ("L];
PutTime[out, stamp.time];
PutString[out, ", "L]; PutMachine[out, stamp];
PutChar[out, ')]};
WriteOneVersion: PROC [
out: Stream.Handle,
version: LONG POINTER TO BcdDefs.VersionStamp, tag: Strings.String] = {
OPEN CharIO;
IF version = NIL THEN RETURN;
PutString[out, tag];
PutTime[out, version.time];
PutString[out, " on "L];
PutMachine[out, version↑];
PutChar[out, '\n]};
PutVersions: PUBLIC PROC [
out: Stream.Handle,
version, creator, source: LONG POINTER TO BcdDefs.VersionStamp ← NIL] = {
WriteOneVersion[out, version, " created "L];
WriteOneVersion[out, creator, " creator "L];
WriteOneVersion[out, source, " source "L];
CharIO.PutChar[out, '\n]};
PutTime: PUBLIC PROC [out: Stream.Handle, time: LONG CARDINAL] = {
s: STRING = [40];
Time.Append[s, Time.Unpack[[time]]];
CharIO.PutString[out, s]};
PutMachine: PUBLIC PROC [out: Stream.Handle, stamp: BcdDefs.VersionStamp] = {
OPEN CharIO;
octal: Format.NumberFormat = [8, FALSE, FALSE, 1];
PutNumber[out, stamp.net, octal]; PutChar[out, '#];
PutNumber[out, stamp.host, octal]; PutChar[out, '#]};
PutFileID: PUBLIC PROC [out: Stream.Handle] = {
OPEN CharIO;
PutString[out, filename];
PutString[out, ", version "L]; PutVersionId[out, version];
PutString[out, "\n source "L]; PutTime[out, source.time];
PutString[out, "\n creator "L]; PutVersionId[out, creator];
PutString[out, "\n\n"L]};
PutHti: PUBLIC PROC [out: Stream.Handle, name: Symbols.Name] = {
OPEN CharIO;
desc: Strings.SubStringDescriptor;
s: Strings.SubString = @desc;
IF name = Symbols.nullName THEN PutString[out, "(anonymous)"L]
ELSE {symbols.SubStringForName[s, name]; PutSubString[out, s]}};
PutSei: PUBLIC PROC [out: Stream.Handle, sei: Symbols.ISEIndex] = {
PutHti[out, IF sei = Symbols.SENull THEN Symbols.nullName ELSE symbols.seb[sei].hash]};
-- TTY interface
Message: PUBLIC PROC [s: Strings.String] = {
FOR i: CARDINAL IN [0..s.length) DO (Exec.w).PutChar[s[i]] ENDLOOP};
ttyObject: Stream.Object ← Stream.defaultObject;
TTYPutByte: Stream.PutByteProcedure = {
(Exec.w).PutChar[0c + byte]};
TTYReset: Stream.DeleteProcedure = {
ttyObject ← Stream.defaultObject};
TTYStream: PUBLIC PROC RETURNS [Stream.Handle] = {
ttyObject.putByte ← TTYPutByte;
ttyObject.delete ← TTYReset;
RETURN [@ttyObject]};
}.