BcdControl.mesa
Last edited by Satterthwaite on May 10, 1983 10:24 am 
Last Edited by: Maxwell, August 29, 1983 2:52 pm
DIRECTORY
Alloc: TYPE USING [Create, Chunkify, Destroy, Failure, Reset, TableInfo],
Ascii: TYPE USING [Lower],
BasicTime: TYPE USING [GMT, Now, Period],
BcdBindDefs: TYPE USING [RelocHandle],
BcdComData: TYPE USING [
aborted, bcdName, binderVersion, codeName, commandArgs, copyCode, copySymbols,
currentName, debug, errors, errorStream, logStream, nConfigs, nErrors,
nExports, nImports, nModules, nPages, nWarnings, objectStamp, op,
outputFile, outputFti, rootName, sourceName, sourceStream,
sourceVersion, symbolName, table, textIndex, warnings, zone],
BcdControlDefs: TYPE USING [
BindRoot, BuildSemanticEntries, LoadRoot, WriteBcd,
PrintBcd, PrintRelocations, PrintSemanticEntries, PrintTree],
BcdDefs: TYPE USING [
BinderNTables, httype, MTNull, NullName, sstype, treetype, VersionStamp],
BcdErrorDefs: TYPE USING [Finalize, GetModule, GetSti, Initialize],
BcdFileDefs: TYPE USING [BuildFileTable, EraseFileTable],
BcdLiterals: TYPE USING [Initialize, Finalize, SealLiterals],
BcdParseData: TYPE,
BcdUtilDefs: TYPE USING [EnterFile, Init, Reset],
Commander: TYPE USING [Handle, Register],
CommandUtil: TYPE USING [Echo, Failed, GetNthPair, ListLength, PairList, Parse, SetExtension],
DebuggerSwap: TYPE USING [CallDebugger],
FileIO: TYPE USING [StreamFromOpenFile],
FS: TYPE USING [Close, Delete, EnumerateForInfo, Error, InfoProc, nullOpenFile, Open, OpenFile],
HashOps: TYPE USING [Finalize, Initialize],
IO: TYPE USING [char, Close, CR, CreateProcsStream, CreateRefStreamProcs, int, Put, PutChar, PutRope, rope, SetLength, STREAM, StreamProcs, time, UserAbort],
OSMiscOps: TYPE USING [
BcdCreateTime, DeleteFile, FindFile, --ImageId,-- MergeStamps, TimeToStamp],
P1: TYPE USING [InstallParseTable, Parse],
PrincOpsUtils: TYPE USING [CodeBase],
Rope: TYPE USING [Equal, Fetch, Find, Flatten, Length, ROPE, Substr],
Symbols: TYPE USING [stNull],
Tree: TYPE USING [Link],
TreeOps: TYPE USING [PopTree, Finalize, Initialize],
UnsafeStorage: TYPE USING [FreeUZone, NewUZone];
BcdControl: PROGRAM
IMPORTS
Alloc, Ascii, BasicTime, BcdControlDefs, BcdErrorDefs, BcdFileDefs, BcdLiterals, BcdParseData, BcdUtilDefs, Commander, CommandUtil, DebuggerSwap, FileIO, FS, HashOps, IO, OSMiscOps, P1, PrincOpsUtils, Rope, TreeOps, UnsafeStorage,
data: BcdComData = {
EquivalentRope: PROC [s1, s2: Rope.ROPE] RETURNS [BOOL] ~ {
RETURN [Rope.Equal[s1, s2, FALSE]]};
command gathering and logging
log: IO.STREAMNIL;
logFile: FS.OpenFile ← FS.nullOpenFile;
SetLogStream: PROC ~ {
IF logFile = FS.nullOpenFile THEN
logFile ← OSMiscOps.FindFile["Binder.Log", $write];
log ← FileIO.StreamFromOpenFile[logFile]; IO.SetLength[log, 0]};
LogRope: PROC [s: Rope.ROPE] ~ {IO.PutRope[log, s]};
LogChar: PROC [c: CHAR] ~ {IO.PutChar[log, c]};
LogNumber: PROC [n: INTEGER] ~ {IO.Put[log, IO.int[n]]};
renamedOutput: BOOLFALSE;
RepeatCommand: PROC [s: IO.STREAM] ~ {
OPEN IO;
PutRope[s, "\nBinding "]; PutRope[s, data.sourceName];
IF renamedOutput THEN {PutRope[s, ", BCD to "]; PutRope[s, data.bcdName]};
IF data.copyCode THEN {
PutRope[s, ", code to "];
PutRope[s, IF data.codeName = NIL THEN "BCD" ELSE data.codeName]};
IF data.copySymbols THEN {
PutRope[s, ", symbols to "];
PutRope[s, IF data.symbolName = NIL THEN "BCD" ELSE data.symbolName]};
PutChar[s, '\n]};
error logging
errorName: Rope.ROPENIL;
errorFile: FS.OpenFile ← FS.nullOpenFile;
errorStream: IO.STREAMNIL;
errorStreamProcs: REF IO.StreamProcs ← NIL;
SetErrorName: PROC ~ {
IF errorName = NIL THEN {
errorName ← CommandUtil.SetExtension[data.rootName, "errlog"]}};
DummyErrorStream: PROC [
putProc: SAFE PROC[self: IO.STREAM, char: CHAR]] RETURNS [IO.STREAM] ~ {
IF errorStreamProcs = NIL THEN
errorStreamProcs ← IO.CreateRefStreamProcs[putChar~putProc];
RETURN [IO.CreateProcsStream[errorStreamProcs, NIL]]};
OpenErrorStream: PROC ~ {
IF errorName = NIL THEN SetErrorName[];
IF errorFile = FS.nullOpenFile THEN
errorFile ← OSMiscOps.FindFile[errorName, $write];
errorStream ← FileIO.StreamFromOpenFile[errorFile];
IO.SetLength[errorStream, 0];
WriteHerald[errorStream, errorName]; RepeatCommand[errorStream]};
CloseErrorStream: PROC ~ INLINE {IF data.errorStream # NIL THEN ErrorDestroy[]};
ErrorPut: SAFE PROC[self: IO.STREAM, char: CHAR] ~ TRUSTED {
IF errorStream = NIL THEN OpenErrorStream[];
errorStream.PutChar[char]};
ErrorDestroy: PROC ~ {
IF errorFile # FS.nullOpenFile AND errorStream # NIL THEN {
IO.Close[errorStream]; errorStream ← NIL}};
WriteHerald: PROC [stream: IO.STREAM, id: Rope.ROPE] ~ {
stream.Put[IO.rope["Cedar 3.x Binder of "], IO.time[OSMiscOps.BcdCreateTime[]]];
IF fbh = NIL AND feedback.create # NIL
THEN fbh ← feedback.create[system~"Binder"L, herald~herald];
IF id.Length[] # 0 THEN stream.Put[IO.rope[id], IO.rope[" -- "]];
stream.Put[IO.time[], IO.char['\n]]};
Logger: PROC [proc: PROC [log: IO.STREAM]] = {
proc[data.errorStream]};
file bookkeeping
sourceFile, bcdFile: FS.OpenFile ← FS.nullOpenFile;
GetRoot: PROC [s: Rope.ROPE] RETURNS[Rope.ROPE] ~ {
dotIndex: INT ← Rope.Find[s, "."];
IF dotIndex > -1 THEN s ← Rope.Substr[s, 0, dotIndex];
RETURN[s]};
switches
StandardDefaults: PACKED ARRAY CHAR ['a..'z] OF BOOL ~ [
FALSE, -- A Copy all (code and symbols)
FALSE, -- B Unused
TRUE , -- C Copy code
FALSE, -- D Call debugger error
FALSE, -- E Unused
FALSE, -- F Unused
TRUE , -- G TRUE => errlog goes to binder.log, FALSE => use root.errlog
FALSE, -- H Unused
FALSE, -- I Unused
FALSE, -- J Unused
FALSE, -- K Unused
FALSE, -- L Unused
FALSE, -- M Unused
FALSE, -- N Unused
FALSE, -- O Unused
FALSE, -- P Pause after config with errors
FALSE, -- Q Unused
FALSE, -- R Unused
FALSE, -- S Copy symbols
FALSE, -- T Unused
FALSE, -- U Unused
FALSE, -- V Unused
FALSE, -- W Pause after config with warnings
FALSE, -- X Copy compressed symbols (not implemented)
FALSE, -- Y Unused
FALSE -- Z Unused
];
Exec interfaces
Bind: SAFE PROC [cmd: Commander.Handle] ~ TRUSTED {
results: CommandUtil.PairList;
switches: Rope.ROPE;
key, value: Rope.ROPE;
anyErrors, anyWarnings: BOOLFALSE;
localPause: BOOL;
defaultSwitches: PACKED ARRAY CHAR ['a..'z] OF BOOL ← StandardDefaults;
defaultDebugPass: CARDINALCARDINAL.LAST;
localSwitches: PACKED ARRAY CHAR ['a..'z] OF BOOL;
debugPass: CARDINAL;
IF cmd = NIL THEN ERROR;
WHILE theCommand.data[theCommand.len] # '\n DO
theCommand.len ← theCommand.len + 1;
ENDLOOP;
SetLogStream[];
WriteHerald[log, NIL];
data.op ← $bind;
data.zone ← UnsafeStorage.NewUZone[];
data.table ← NIL;
main loop
DO {
Initialize: PROC ~ {
weights: ARRAY [0..BcdDefs.BinderNTables) OF Alloc.TableInfo ← [ -- empirical (SDD)
[4], [2], [10], [4], [5], [4], [4], [4], [2], [1],
[4], [4], [4], [2], [4], [2], [4], [5], [1], [1]];
RepeatCommand[log];
data.errors ← data.warnings ← FALSE;
data.nErrors ← data.nWarnings ← 0;
data.textIndex ← 0;
data.currentName ← BcdDefs.NullName;
IF data.table = NIL THEN {
data.table ← Alloc.Create[weights~DESCRIPTOR[weights]];
(data.table).Chunkify[BcdDefs.treetype]}
ELSE (data.table).Reset[];
HashOps.Initialize[data.table, BcdDefs.httype, BcdDefs.sstype];
TreeOps.Initialize[data.table, data.zone];
BcdUtilDefs.Init[data.table];
BcdLiterals.Initialize[data.table, data.zone];
BcdErrorDefs.Initialize[];
errorStream ← NIL};
Finalize: PROC ~ {
TreeOps.Finalize[]; HashOps.Finalize[];
BcdErrorDefs.Finalize[];
BcdLiterals.Finalize[]; BcdUtilDefs.Reset[];
(data.table).Reset[];
anyErrors ← data.errors OR anyErrors; anyWarnings ← data.warnings OR anyWarnings;
IF data.aborted THEN LogRope["Binding aborted\n"]
ELSE IF data.errors THEN LogRope["Errors detected; BCD not written\n"]
ELSE {
IF data.nConfigs > 1 THEN {
LogNumber[data.nConfigs]; LogRope[" configs, "]};
LogNumber[data.nModules]; LogRope[" modules, "];
LogNumber[data.nImports]; LogRope[" imports, "];
LogNumber[data.nExports]; LogRope[" exports, "];
LogNumber[data.nPages]; LogRope[" pages, "];
LogNumber[BasicTime.Period[BasicTime.Now[], startTime]]; LogRope[" seconds\n"]};
IF errorStream # NIL THEN {
LogRope["See "]; LogRope[data.rootName]; LogRope[".errlog\n"]};
IF log # data.errorStream THEN CloseErrorStream[];
IF errorName = NIL THEN {SetErrorName[]; OSMiscOps.DeleteFile[errorName]};
IO.Close[data.sourceStream];
IF sourceFile # FS.nullOpenFile
THEN {FS.Close[sourceFile]; sourceFile ← FS.nullOpenFile};
IF bcdFile # FS.nullOpenFile
THEN {FS.Close[bcdFile]; bcdFile ← FS.nullOpenFile};
IF data.nErrors # 0 OR data.aborted THEN
FS.Delete[data.bcdName ! FS.Error => TRUSTED {CONTINUE}];
data.outputFile ← FS.nullOpenFile;
IF feedback.finishItem # NIL THEN {
outcome: ExecOps.Outcome ~
SELECT TRUE FROM
data.aborted => $aborted,
data.errors AND data.warnings => $errorsAndWarnings,
data.errors => $errors,
data.warnings => $warnings,
ENDCASE => $ok;
IF data.errors
THEN cmd.out.Put[IO.rope["no errors"]]
ELSE cmd.out.Put[IO.int[data.nErrors], IO.rope[" errors"]];
IF data.nWarnings # 0
THEN cmd.out.Put[IO.rope[", "], IO.int[data.nWarnings], IO.rope[" warnings"]];
cmd.out.Put[IO.char['\n]];
};
Debug: PROC [printRel, printBcd: BOOL] ~ {
BcdControlDefs.PrintTree[root];
BcdControlDefs.PrintSemanticEntries[];
IF printRel THEN BcdControlDefs.PrintRelocations[relocationHead];
IF printBcd THEN BcdControlDefs.PrintBcd[]};
parsed: BOOL;
root: Tree.Link;
first: BOOLTRUE;
relocationHead: BcdBindDefs.RelocHandle;
startTime: BasicTime.GMT;
localSwitches ← defaultSwitches;
localPause ← FALSE; debugPass ← defaultDebugPass;
renamedOutput ← FALSE;
data.aborted ← FALSE;
[data.sourceName, data.commandArgs, results, switches] ←
CommandUtil.Parse[command: cmd.commandLine
! CommandUtil.Failed => {GO TO badSyntax}];
IF data.sourceName = NIL AND switches = NIL THEN EXIT; -- done binding
LogRope["\nCommand: "];
CommandUtil.Echo[
d~log, operator~data.sourceName, argList~data.commandArgs,
resultList~results, switches~switches];
IF data.sourceName = NIL THEN GO TO globalSwitches;
FOR n: CARDINAL IN [0..CommandUtil.ListLength[results]) DO
[key, value] ← CommandUtil.GetNthPair[list~results, n~n];
SELECT TRUE FROM
(key = NIL), EquivalentRope[key, "bcd"] => {
data.bcdName ← value; renamedOutput ← TRUE};
EquivalentRope[key, "code"] => {
data.codeName ← value; localSwitches['c] ← TRUE};
EquivalentRope[key, "symbols"] => {
data.symbolName ← value; localSwitches['s] ← TRUE};
ENDCASE => GO TO badSemantics;
ENDLOOP;
data.rootName ← GetRoot[
(IF data.bcdName # NIL THEN data.bcdName ELSE data.sourceName)];
IF switches # NIL THEN {
sense: BOOLTRUE;
length: INT ← switches.Length[];
FOR i: INT IN [0..length) DO
c: CHAR ~ switches.Fetch[i];
SELECT c FROM
'-, '~ => sense ← ~sense;
IN ['a..'z] => {localSwitches[c] ← sense; sense ← TRUE};
IN ['A..'Z] => {localSwitches[Ascii.Lower[c]] ← sense; sense ← TRUE};
'! => {DebuggerSwap.CallDebugger[NIL]; sense ← TRUE};
IN ['1..'4] => {debugPass ← c-'0; sense ← TRUE};
ENDCASE;
ENDLOOP};
data.sourceName ← CommandUtil.SetExtension[data.sourceName, "config"];
data.bcdName ← CommandUtil.SetExtension[data.bcdName, "bcd"];
data.copyCode ← (localSwitches['c] OR localSwitches['a]);
data.copySymbols ← (localSwitches['s] OR localSwitches['x] OR localSwitches['a]);
IF localSwitches['x] THEN
LogRope["\nSymbol compression not available in this version"];
IF data.copyCode AND data.codeName # NIL THEN {
data.codeName ← CommandUtil.SetExtension[data.codeName, "code"];
IF EquivalentRope[data.codeName, data.bcdName] THEN data.codeName ← NIL};
IF data.copySymbols AND ~(localSwitches['a] AND data.symbolName = NIL) THEN {
data.symbolName ← CommandUtil.SetExtension[data.symbolName, "symbols"];
IF EquivalentRope[data.symbolName, data.bcdName] THEN data.symbolName ← NIL};
cmd.out.Put[IO.rope["Binding: "], IO.rope[data.rootName]];
FOR c: CHAR IN ['a..'z] DO
IF localSwitches[c] # StandardDefaults[c] THEN {
IF first THEN {first ← FALSE; cmd.out.PutChar['/]};
IF StandardDefaults[c] THEN cmd.out.PutChar['-];
cmd.out.PutChar[c]};
ENDLOOP;
cmd.out.PutChar[IO.CR];
startTime ← BasicTime.Now[];
BEGIN
ENABLE ANY => {CONTINUE};
createTime: BasicTime.GMT;
GetCreateTime: FS.InfoProc = TRUSTED {createTime ← created; RETURN[continue: TRUE]};
sourceFile ← FS.nullOpenFile;
errorFile ← data.outputFile ← FS.nullOpenFile;
data.sourceStream ← NIL;
sourceFile ← FS.Open[data.sourceName, ! FS.Error => TRUSTED {CONTINUE}];
FS.EnumerateForInfo[data.sourceName, GetCreateTime];
IF sourceFile # NIL THEN {
data.sourceStream ← FileIO.StreamFromOpenFile[sourceFile];
data.sourceVersion ← BcdDefs.VersionStamp[
net~0, host~0, time~LOOPHOLE[createTime]]};
IF data.sourceStream = NIL THEN GO TO noSource;
data.outputFile ← bcdFile ← FS.Open[data.bcdName];
END;
data.logStream ← log;
data.errorStream ←
IF localSwitches['g] THEN log ELSE DummyErrorStream[putProc~ErrorPut];
localPause ← localSwitches['p]; data.debug ← localSwitches['d];
localSwitches['g] ← localSwitches['p] ← localSwitches['d] ← FALSE;
data.objectStamp ← OSMiscOps.TimeToStamp[data.sourceVersion];
encode switches, binder version (see BcdLoad processing also)
data.objectStamp ← OSMiscOps.MergeStamps[
data.objectStamp, OSMiscOps.TimeToStamp[[0, 0, LOOPHOLE[localSwitches]]]];
data.objectStamp ← OSMiscOps.MergeStamps[
data.objectStamp, OSMiscOps.TimeToStamp[data.binderVersion]];
Initialize[];
BEGIN
ENABLE {
BcdErrorDefs.GetModule => {RESUME [BcdDefs.MTNull, 0]};
BcdErrorDefs.GetSti => {RESUME [Symbols.stNull]};
Alloc.Failure => {
data.errors ← TRUE;
IF ~data.debug THEN {
LogRope["Storage Overflow\n"]; GOTO Overflow}};
UNWIND => {Finalize[]}};
cmd.out.PutRope[". "]; -- pass 1
IF cmd.in.UserAbort[] THEN GOTO Abort;
[complete~parsed, nErrors~data.nErrors] ← P1.Parse[data.sourceStream, data.zone, Logger];
IF ~parsed THEN GO TO Failed;
IF data.nErrors > 0 THEN data.errors ← TRUE;
root ← TreeOps.PopTree[];
BcdControlDefs.BuildSemanticEntries[root];
data.outputFti ← BcdUtilDefs.EnterFile[LOOPHOLE[Rope.Flatten[data.bcdName]]];
BcdFileDefs.BuildFileTable[data.table, data.zone];
IF debugPass <= 1 THEN Debug[printRel~FALSE, printBcd~FALSE];
cmd.out.PutRope[". "]; -- pass 2
IF cmd.in.UserAbort[] THEN GOTO AbortAndEraseFileTable;
relocationHead ← BcdControlDefs.LoadRoot[root];
BcdLiterals.SealLiterals[];
IF debugPass <= 2 THEN Debug[printRel~TRUE, printBcd~TRUE];
cmd.out.PutRope[". "]; -- pass 3
IF cmd.in.UserAbort[] THEN GOTO AbortAndEraseFileTable;
BcdControlDefs.BindRoot[relocationHead];
IF debugPass <= 3 THEN Debug[printRel~FALSE, printBcd~TRUE];
IF ~data.errors THEN {
cmd.out.PutRope[". "]; -- pass 4
IF cmd.in.UserAbort[] THEN GOTO AbortAndEraseFileTable;
BcdControlDefs.WriteBcd[root]};
BcdFileDefs.EraseFileTable[];
EXITS
Overflow => NULL;
Failed => data.errors ← TRUE;
Abort => {data.aborted ← TRUE};
AbortAndEraseFileTable => {BcdFileDefs.EraseFileTable[]; data.aborted ← TRUE};
END;
Finalize[];
EXITS
noSource => {
LogRope["\nCan't open "]; LogRope[data.sourceName]; LogChar['\n];
data.errors ← TRUE};
globalSwitches => {
sense: BOOLTRUE;
length: INT ← switches.Length[];
FOR i: INT IN [0..length) DO
c: CHAR ~ switches.Fetch[i];
SELECT c FROM
'-, '~ => sense ← ~sense;
'! => DebuggerSwap.CallDebugger[NIL];
IN ['a..'z] => {defaultSwitches[c] ← sense; sense ← TRUE};
IN ['A..'Z] => {defaultSwitches[Ascii.Lower[c]] ← sense; sense ← TRUE};
IN ['1..'5] => {defaultDebugPass ← c-'0; sense ← TRUE};
ENDCASE => EXIT;
ENDLOOP};
badSemantics => {
data.errors ← TRUE;
LogRope["\n -- Illegal command"]}};
IF data.aborted THEN EXIT; -- stop binding
IF (data.errors OR (data.warnings AND localSwitches['w])) AND localPause THEN EXIT;
REPEAT
badSyntax => {anyErrors ← data.errors ← TRUE; LogRope["-- Illegal syntax"]};
ENDLOOP;
IF data.table # NIL THEN Alloc.Destroy[data.table]; data.table ← NIL;
UnsafeStorage.FreeUZone[data.zone]; data.zone ← NIL;
IO.Close[log]; logFile ← FS.nullOpenFile;
cmd.out.PutRope["End of binding\n"]};
RETURN [SELECT TRUE FROM
data.aborted => $aborted,
(anyErrors AND anyWarnings) => $errorsAndWarnings,
anyErrors => $errors,
anyWarnings => $warnings,
ENDCASE => $ok]};
global Binder initialization
Init: PROC ~ {
START data;
data.binderVersion ← OSMiscOps.ImageId[];
data.binderVersion ← [net~0Ch, host~0Bh, time~000F0003h]; -- Cedar release
P1.InstallParseTable[PrincOpsUtils.CodeBase[LOOPHOLE[BcdParseData]]];
Commander.Register["Binder", Bind, "Bind a list of configurations."]};
Init[];
}.