-- CIFSBcdControl.mesa
-- Last edited by Satterthwaite on May 10, 1983 10:24 am	

DIRECTORY
  Alloc: TYPE USING [Create, Chunkify, Destroy, Failure, Reset, TableInfo],
  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],
  CharIO: TYPE USING [PutChar, PutDecimal, PutLine, PutString],
  CIFS: TYPE USING [
    OpenFile, Close, Delete, Error, GetFC, Open, create, read, replace, write],
  CommandUtil: TYPE USING [
    CommandObject, CopyString, Echo, Failed, FreePairList, FreeString,
    GetNthPair, ListLength, PairList, Parse, SetExtension],
  ConvertUnsafe: TYPE USING [ToRope],
  ExecOps: TYPE USING [Command, Outcome],
  Feedback: TYPE USING [Handle, Outcome, Procs, ProcsHandle],
  File: TYPE USING [Capability, nullCapability],
  FileStream: TYPE USING [Create, GetLeaderProperties, SetLength],
  HashOps: TYPE USING [Finalize, Initialize],
  Heap: TYPE USING [Create, Delete],
  IO: TYPE USING [UserAborted],
  LongString: TYPE USING [AppendDecimal],
  OSMiscOps: TYPE USING [
    BcdCreateTime, DeleteFile, FindFile, --ImageId,-- MergeStamps, TimeToStamp],
  P1: TYPE USING [InstallParseTable, Parse],
  Runtime: TYPE USING [CallDebugger, GetTableBase], 
  Stream: TYPE USING [Handle, Object, PutByteProcedure, Delete, PutByte],
  Strings: TYPE USING [
    AppendChar, AppendString, EquivalentSubString, String, SubStringDescriptor],
  Symbols: TYPE USING [stNull],
  TemporarySpecialExecOps: TYPE USING [],
  Time: TYPE USING [Append, AppendCurrent, Current, Unpack],
  Tree: TYPE USING [Link],
  TreeOps: TYPE USING [PopTree, Finalize, Initialize];

CIFSBcdControl: PROGRAM
    IMPORTS
      Alloc, BcdControlDefs, BcdErrorDefs, BcdFileDefs, BcdLiterals,
      BcdParseData, BcdUtilDefs, CharIO, CIFS, CommandUtil, ConvertUnsafe,
      FileStream, HashOps, Heap, IO, OSMiscOps, P1, Runtime, Stream,
      String: LongString, Strings, Time, TreeOps,
      data: BcdComData
    EXPORTS ExecOps, TemporarySpecialExecOps = {

 
  EquivalentString: PROC [s1, s2: Strings.String] RETURNS [BOOL] ~ {
    ss1: Strings.SubStringDescriptor ← [base~s1, offset~0, length~s1.length];
    ss2: Strings.SubStringDescriptor ← [base~s2, offset~0, length~s2.length];
    RETURN [Strings.EquivalentSubString[@ss1, @ss2]]};


 -- feedback control

  feedback: Feedback.ProcsHandle;
  fbh: Feedback.Handle ← NIL;
 
 
 -- command gathering and logging

  log: Stream.Handle ← NIL;
  logFile: File.Capability ← File.nullCapability;

  SetLogStream: PROC ~ {
    IF logFile = File.nullCapability THEN 
      logFile ← OSMiscOps.FindFile["Binder.Log"L, $write];
    log ← FileStream.Create[logFile]; FileStream.SetLength[log, 0]};

  LogString: PROC [s: Strings.String] ~ {CharIO.PutString[log, s]};
  LogChar: PROC [c: CHAR] ~ {CharIO.PutChar[log, c]};
  LogNumber: PROC [n: INTEGER] ~ {CharIO.PutDecimal[log, n]};

  renamedOutput: BOOL ← FALSE;

  RepeatCommand: PROC [s: Stream.Handle] ~ {
    OPEN CharIO;
    PutString[s, "\nBinding "L]; PutString[s, data.sourceName];
    IF renamedOutput THEN {PutString[s, ", BCD to "L]; PutString[s, data.bcdName]};
    IF data.copyCode THEN {
      PutString[s, ", code to "L];
      PutString[s, IF data.codeName = NIL THEN "BCD"L ELSE data.codeName]};
    IF data.copySymbols THEN {
      PutString[s, ", symbols to "L];
      PutString[s, IF data.symbolName = NIL THEN "BCD"L ELSE data.symbolName]};
    PutChar[s, '\n]};


 -- error logging

  errorName: Strings.String ← NIL;
  errorFile: File.Capability ← File.nullCapability;
  errorStream: Stream.Handle ← NIL;
  errorStreamObject: Stream.Object;

  SetErrorName: PROC ~ {
    IF errorName = NIL THEN {
      errorName ← CommandUtil.CopyString[data.rootName, 1+("errlog"L).length];
      errorName ← CommandUtil.SetExtension[errorName, "errlog"L]}};

  DummyErrorStream: PROC [
      putProc: Stream.PutByteProcedure] RETURNS [Stream.Handle] ~ { 
    errorStreamObject ← [
      options~TRASH, 
      getByte~TRASH, putByte~putProc, 
      getWord~TRASH, putWord~TRASH,
      get~TRASH, put~TRASH, 
      setSST~TRASH, sendAttention~TRASH, waitAttention~TRASH, 
      delete~TRASH];
    RETURN [@errorStreamObject]};

  OpenErrorStream: PROC ~ {
    IF errorName = NIL THEN SetErrorName[];
    IF errorFile = File.nullCapability THEN
      errorFile ← OSMiscOps.FindFile[errorName, $write];
    errorStream ← FileStream.Create[errorFile];
    FileStream.SetLength[errorStream, 0];
    WriteHerald[errorStream, errorName];  RepeatCommand[errorStream]};

  CloseErrorStream: PROC ~ INLINE {IF data.errorStream # NIL THEN ErrorDestroy[]};

  ErrorPut: Stream.PutByteProcedure ~ {
    IF errorStream = NIL THEN OpenErrorStream[];
    errorStream.PutByte[byte]};

  ErrorDestroy: PROC ~ {
    IF errorFile # File.nullCapability AND errorStream # NIL THEN {
      Stream.Delete[errorStream]; errorStream ← NIL}};


  WriteHerald: PROC [s: Stream.Handle, id: Strings.String] ~ {
    OPEN Time, CharIO;
    herald: STRING ← [60];
    Strings.AppendString[herald, "Cedar 3.x Binder of "L];
    Time.Append[herald, Time.Unpack[[OSMiscOps.BcdCreateTime[]]]];
    herald.length ← herald.length-3;
    PutLine[s, herald];
    IF fbh = NIL AND feedback.create # NIL
      THEN fbh ← feedback.create[system~"Binder"L, herald~herald];
    IF id # NIL THEN {PutString[s, id]; PutString[s, " -- "L]};
    herald.length ← 0; Time.AppendCurrent[herald]; PutLine[s, herald]};

  Logger: PROC [proc: PROC [log: Stream.Handle]] = {
    proc[data.errorStream]};
    
    
 -- file bookkeeping

  sourceFile, bcdFile: CIFS.OpenFile ← NIL;

  SetRoot: PROC [root, s: Strings.String] ~ {
    root.length ← 0;
    FOR i: CARDINAL IN [0..s.length) DO 
      IF s[i] = '. THEN EXIT;  
      Strings.AppendChar[root, s[i]]; 
      ENDLOOP};

  SetFileName: PROC [
      fileName, extension: Strings.String] RETURNS [Strings.String] ~ {
    root: Strings.String ~ 
      (IF fileName = NIL THEN 
	 CommandUtil.CopyString[data.rootName, 1+extension.length]
       ELSE fileName);
    RETURN [CommandUtil.SetExtension[root, extension]]};

   
 -- 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: PUBLIC PROC [cmd: ExecOps.Command] RETURNS [outcome: ExecOps.Outcome] ~ { 
  fProcs: Feedback.Procs ← [];	-- all NIL
  RETURN [BindUsingFeedback[cmd, @fProcs]]};
    

BindUsingFeedback: PUBLIC PROC [cmd: ExecOps.Command, feedbackProcs: Feedback.ProcsHandle] 
    RETURNS [outcome: ExecOps.Outcome] ~ { 
  theCommand: CommandUtil.CommandObject ← [pos~0, len~0, data~cmd];
  results: CommandUtil.PairList;
  switches: Strings.String;
  key, value: Strings.String;
  anyErrors, anyWarnings: BOOL ← FALSE;
  localPause: BOOL;
  
  defaultSwitches: PACKED ARRAY CHAR ['a..'z] OF BOOL ← StandardDefaults;
  defaultDebugPass: CARDINAL ← CARDINAL.LAST;
  localSwitches: PACKED ARRAY CHAR ['a..'z] OF BOOL;
  debugPass: CARDINAL;

  fbh ← NIL; feedback ← feedbackProcs;
  
  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 ← Heap.Create[initial~16, increment~8];
  data.table ← NIL;
  data.rootName ← (data.zone).NEW[StringBody[100]];

 -- 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 LogString["Binding aborted\n"L]
      ELSE IF data.errors THEN LogString["Errors detected; BCD not written\n"L]
      ELSE {
	IF data.nConfigs > 1 THEN {
	  LogNumber[data.nConfigs]; LogString[" configs, "L]};
        LogNumber[data.nModules]; LogString[" modules, "L];
        LogNumber[data.nImports]; LogString[" imports, "L];
        LogNumber[data.nExports]; LogString[" exports, "L];
        LogNumber[data.nPages]; LogString[" pages, "L];
	LogNumber[Time.Current[] - startTime]; LogString[" seconds\n"L]};
      IF errorStream # NIL THEN {
	LogString["See "L]; LogString[data.rootName]; LogString[".errlog\n"L]};
      IF log # data.errorStream THEN CloseErrorStream[];
      IF errorName = NIL THEN {SetErrorName[]; OSMiscOps.DeleteFile[errorName]};
      Stream.Delete[data.sourceStream];
      IF sourceFile # NIL THEN {CIFS.Close[sourceFile]; sourceFile ← NIL};
      IF bcdFile # NIL THEN {CIFS.Close[bcdFile]; bcdFile ← NIL};
      IF data.nErrors # 0 OR data.aborted THEN
        CIFS.Delete[ConvertUnsafe.ToRope[data.bcdName] ! CIFS.Error => TRUSTED {CONTINUE}];
      data.outputFile ← File.nullCapability;
      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;
	msg: STRING ← [30];
	IF ~data.errors
	  THEN Strings.AppendString[msg, "no"L]
	  ELSE String.AppendDecimal[msg, data.nErrors];
	Strings.AppendString[msg, " errors"L];
	IF data.nWarnings # 0
	  THEN {
	    Strings.AppendString[msg, ", "L];
	    String.AppendDecimal[msg, data.nWarnings];
	    Strings.AppendString[msg, " warnings"L]};
	feedback.finishItem[fbh, outcome, msg]}};

    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;
    relocationHead: BcdBindDefs.RelocHandle;
    startTime: LONG CARDINAL;

    localSwitches ← defaultSwitches;
    localPause ← FALSE;  debugPass ← defaultDebugPass;
    renamedOutput ← FALSE;
    data.aborted ← FALSE;

    [data.sourceName, data.commandArgs, results, switches] ←
      CommandUtil.Parse[
		s~@theCommand,
		opX~2+("config"L).length, resultX~2+("symbols"L).length
	  ! CommandUtil.Failed => {GO TO badSyntax}];
    IF data.sourceName = NIL AND switches = NIL THEN EXIT;  -- done binding
    LogString["\nCommand: "L];
    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), EquivalentString[key, "bcd"L] => { 
	  data.bcdName ← value; renamedOutput ← TRUE};
	EquivalentString[key, "code"L] => { 
	  data.codeName ← value; localSwitches['c] ← TRUE};
	EquivalentString[key, "symbols"L] => {  
	  data.symbolName ← value; localSwitches['s] ← TRUE};
	ENDCASE => GO TO badSemantics;
      ENDLOOP;
    SetRoot[
      data.rootName,
      (IF data.bcdName # NIL THEN data.bcdName ELSE data.sourceName)];
    IF switches # NIL THEN {
	i: CARDINAL ← 0;
	sense: BOOL ← TRUE;
	WHILE i < switches.length DO
	  c: CHAR ~ switches[i];
	  SELECT c FROM
	    '-, '~ => sense ← ~sense;
	    IN ['a..'z] => {localSwitches[c] ← sense; sense ← TRUE};
	    IN ['A..'Z] => {localSwitches[c + ('a-'A)] ← sense; sense ← TRUE};
	    '! => {Runtime.CallDebugger[NIL]; sense ← TRUE};
	    IN ['1..'4] => {debugPass ← c-'0; sense ← TRUE};
	    ENDCASE;
	  i ← i+1;
	  ENDLOOP;
	switches ← CommandUtil.FreeString[switches]};

    data.sourceName ← SetFileName[data.sourceName, "config"L];
    data.bcdName ← SetFileName[data.bcdName, "bcd"L];
    data.copyCode ← (localSwitches['c] OR localSwitches['a]);
    data.copySymbols ← (localSwitches['s] OR localSwitches['x] OR localSwitches['a]);
    IF localSwitches['x] THEN
      LogString["\nSymbol compression not available in this version"L];
    IF data.copyCode AND data.codeName # NIL THEN {
      data.codeName ← SetFileName[data.codeName, "code"L];
      IF EquivalentString[data.codeName, data.bcdName] THEN
        data.codeName ← CommandUtil.FreeString[data.codeName]};
    IF data.copySymbols AND ~(localSwitches['a] AND data.symbolName = NIL) THEN {
      data.symbolName ← SetFileName[data.symbolName, "symbols"L];
      IF EquivalentString[data.symbolName, data.bcdName] THEN
        data.symbolName ← CommandUtil.FreeString[data.symbolName]};

    IF feedback.beginItem # NIL THEN {
      item: STRING ← [100];
      first: BOOL ← TRUE;
      Strings.AppendString[item, "Binding: "L]; Strings.AppendString[item, data.rootName];
      FOR c: CHAR IN ['a..'z] DO
	IF localSwitches[c] # StandardDefaults[c] THEN {
	  IF first THEN {first ← FALSE; Strings.AppendChar[item, '/]};
	  IF StandardDefaults[c] THEN Strings.AppendChar[item, '-];
	  Strings.AppendChar[item, c]};
	ENDLOOP;
      feedback.beginItem[fbh, item]}; 

    startTime ← Time.Current[];

    BEGIN
    ENABLE ANY => {CONTINUE};
    sourceFile ← NIL;
    errorFile ← data.outputFile ← File.nullCapability;
    data.sourceStream ← NIL;
    sourceFile ← CIFS.Open[ConvertUnsafe.ToRope[data.sourceName], CIFS.read
      ! CIFS.Error => TRUSTED {CONTINUE}];
    IF sourceFile # NIL THEN {
      data.sourceStream ← FileStream.Create[CIFS.GetFC[sourceFile]];
      data.sourceVersion ← BcdDefs.VersionStamp[
	net~0, host~0,
	time~FileStream.GetLeaderProperties[data.sourceStream].create]};
    IF data.sourceStream = NIL THEN GO TO noSource;
    bcdFile ← CIFS.Open[ConvertUnsafe.ToRope[data.bcdName], CIFS.create+CIFS.replace+CIFS.write];
    IF bcdFile # NIL THEN data.outputFile ← CIFS.GetFC[bcdFile];
    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 {
	      LogString["Storage Overflow\n"L]; GOTO Overflow}};
	  UNWIND => {Finalize[]}};

      IF feedback.noteProgress # NIL THEN
        feedback.noteProgress[fbh, 1 ! IO.UserAborted => {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[data.bcdName];
      BcdFileDefs.BuildFileTable[data.table, data.zone];
        IF debugPass <= 1 THEN Debug[printRel~FALSE, printBcd~FALSE];

      IF feedback.noteProgress # NIL THEN
        feedback.noteProgress[fbh, 2 ! IO.UserAborted => {GOTO AbortAndEraseFileTable}];
      relocationHead ← BcdControlDefs.LoadRoot[root];
      BcdLiterals.SealLiterals[];
        IF debugPass <= 2 THEN Debug[printRel~TRUE, printBcd~TRUE];

      IF feedback.noteProgress # NIL THEN
        feedback.noteProgress[fbh, 3 ! IO.UserAborted => {GOTO AbortAndEraseFileTable}];
      BcdControlDefs.BindRoot[relocationHead];
        IF debugPass <= 3 THEN Debug[printRel~FALSE, printBcd~TRUE];

      IF ~data.errors THEN { 
        IF feedback.noteProgress # NIL THEN
          feedback.noteProgress[fbh, 4 ! IO.UserAborted => {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 => {
        LogString["\nCan't open "L]; LogString[data.sourceName]; LogChar['\n];
        data.errors ← TRUE};
      globalSwitches => {
	sense: BOOL ← TRUE;
        results ← CommandUtil.FreePairList[results];
        FOR i: CARDINAL IN [0..switches.length) DO
	  c: CHAR ~ switches[i];
	  SELECT c FROM
	    '-, '~ => sense ← ~sense;
	    '! => Runtime.CallDebugger[NIL];
	    IN ['a..'z] => {defaultSwitches[c] ← sense; sense ← TRUE};
	    IN ['A..'Z] => {defaultSwitches[c + ('a-'A)] ← sense; sense ← TRUE};
	    IN ['1..'5] => {defaultDebugPass ← c-'0; sense ← TRUE};
	    ENDCASE => EXIT;
	  ENDLOOP;
        switches ← CommandUtil.FreeString[switches]};
      badSemantics => {
        results ← CommandUtil.FreePairList[results];
        data.errors ← TRUE;
        LogString["\n -- Illegal command"L]}};
    data.sourceName ← CommandUtil.FreeString[data.sourceName];
    data.bcdName ← CommandUtil.FreeString[data.bcdName];
    data.codeName ← CommandUtil.FreeString[data.codeName];
    data.symbolName ← CommandUtil.FreeString[data.symbolName];
    data.commandArgs ← CommandUtil.FreePairList[data.commandArgs];
    errorName ← CommandUtil.FreeString[errorName];
    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; LogString["-- Illegal syntax"L]};
    ENDLOOP;

  IF data.table # NIL THEN Alloc.Destroy[data.table];  data.table ← NIL;
  (data.zone).FREE[@data.rootName];
  Heap.Delete[data.zone]; data.zone ← NIL;
  Stream.Delete[log];  logFile ← File.nullCapability;
  IF feedback.destroy # NIL THEN feedback.destroy[fbh, "End of binding"L];
  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[Runtime.GetTableBase[LOOPHOLE[BcdParseData]]]};
      
  Init[];

  }.