-- CrossPackControl.mesa  
-- Last edited by Lewis on  29-Mar-82 16:44:44
-- Last edited by Satterthwaite, January 12, 1983 12:12 pm

DIRECTORY
  Alloc: TYPE USING [
    Chunkify, Create, defaultChunkType, Destroy, Failure, Overflow, Reset,
    TableInfo],
  BcdDefs: TYPE USING [VersionStamp],
  CharIO: TYPE USING [PutChar, PutDecimal, PutLine, PutString],
  CIFS: TYPE USING [OpenFile, Close, Error, GetFC, Open, create, read, write],
  CodePackProcs: TYPE USING [Determine, Destroy],
  CommandUtil: TYPE USING [
    CommandObject, CopyString, Failed, FreePairList, FreeString, GetNthPair,
    ListLength, PairList, Parse, SetExtension],
  ConvertUnsafe: TYPE USING [ToRope],
  Exec: TYPE USING [AddCommand, commandLine],
  ExecOps: TYPE USING [CheckForAbort, Command, Outcome],
  FileStream: TYPE USING [Create, GetLeaderProperties, SetLength],
  HashOps: TYPE USING [Initialize, Finalize],
  Heap: TYPE USING [Create, Delete, FreeNode, MakeNode],
  Inline: TYPE USING [BITXOR, HighByte, LowByte, LowHalf],
  ModuleSymbols: TYPE USING [Initialize, Finalize],
  PackagerDefs: TYPE USING [
    nullSourceIndex, GlobalData, GlobalDataRecord, packagerNTables,
    packhttype, packsstype],
  PackCode: TYPE USING [
    ComputeCodePlacement, Finalize, PackError, WriteBcdToFile,
    WriteCodeToBcdFile],
  PackDebug: TYPE USING [
    Initialize, Finalize, PrintConfigTree, PrintSourceBcd, PrintTree,
    PrintProcessingOrder],
  PackParseData: TYPE,
  P1: TYPE USING [InstallParseTable, Parse],
  ProcessingOrder: TYPE USING [Determine, Destroy],
  ProcessorFace: TYPE USING [processorID],
  PackList : TYPE USING [Print],
  Rope: TYPE USING [ROPE, Fetch, Length],
  Runtime: TYPE USING [GetBcdTime, GetTableBase],
  SemanticEntry: TYPE USING [BuildSemanticEntries],
  SourceBcd: TYPE USING [
    BuildConfigTree, CTreeIndex, DestroyConfigTree, nullCTreeIndex,
    Load, Unload, BadSourceBcd, moduleCount],
  Stream: TYPE USING [
    Delete, DeleteProcedure, Handle, Object, PutBlock, PutByteProcedure, PutByte,
    PutProcedure],
  Strings: TYPE USING [
    AppendChar, AppendString, EquivalentSubString, String, SubStringDescriptor],
  Time: TYPE USING [Append, AppendCurrent, Current, Unpack],
  TreeOps: TYPE USING [Initialize, Finalize, PopTree];

PackControl: MONITOR
  IMPORTS
    Alloc, CharIO, CIFS, CodePackProcs, CommandUtil, ConvertUnsafe, Exec, ExecOps,
    FileStream, HashOps, Heap, Inline, ModuleSymbols, PackCode, PackDebug,
    PackParseData, P1, ProcessingOrder, ProcessorFace, PackList, Rope, Runtime,
    SemanticEntry, SourceBcd, Stream, Strings, Time, TreeOps
  EXPORTS PackagerDefs
  SHARES ProcessorFace = {

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

  GetFile: PROC [name: Strings.String, access: {read, write}] RETURNS [CIFS.OpenFile] ~ {
    RETURN [CIFS.Open[
      ConvertUnsafe.ToRope[name], 
      IF access=$read THEN CIFS.read ELSE CIFS.create+CIFS.write]]};
    
  GetNetAndHost: PROC RETURNS [net, host: CARDINAL] ~ {
    sum: UNSPECIFIED ~ Inline.BITXOR[
      ProcessorFace.processorID.a, Inline.BITXOR[
      ProcessorFace.processorID.b, ProcessorFace.processorID.c]];
    net ← LOOPHOLE[Inline.HighByte[sum]];
    host ← LOOPHOLE[Inline.LowByte[sum]]};


  -- Packager log

  logFile: CIFS.OpenFile;
  log: Stream.Handle ← NIL;

  OpenLogStream: PROC ~ INLINE {
    logFile ← GetFile["/local/Packager.log"L, $write];
    log ← FileStream.Create[logFile.GetFC];
    FileStream.SetLength[log, 0]};
    
  CloseLogStream: PROC ~ INLINE {
    Stream.Delete[log]; CIFS.Close[logFile]; logFile ← NIL};
    

  LogString: PROC [s: Strings.String] ~ {CharIO.PutString[log, s]};
  LogRope: PROC [r: Rope.ROPE] ~ {
    FOR i: INT IN [0..r.Length) DO CharIO.PutChar[log, r.Fetch[i]] ENDLOOP};
  LogLine: PROC [s: Strings.String] ~ {LogString[s]; LogChar['\n]};
  LogChar: PROC [c: CHAR] ~ {CharIO.PutChar[log, c]};
  LogDecimal: PROC [n: CARDINAL] ~ {CharIO.PutDecimal[log, n]};


 -- Command gathering/echoing

  args, results: CommandUtil.PairList;
  switches: Strings.String;

  globalPause, localPause, listPacks: BOOL;
  debugPass: CARDINAL;

  WriteHerald: PROC [s: Stream.Handle, id: Strings.String←NIL] ~ {
    OPEN Time, CharIO;
    herald: STRING ← [80];
    Strings.AppendString[herald, "Cedar Trinity Cross Packager of "L];
    Time.Append[herald, Time.Unpack[[gd.packagerVersion.time]]];
    herald.length ← herald.length-3;
    PutString[s, herald];  PutChar[s, '\n];
    IF id # NIL THEN {PutString[s, id]; PutString[s, " -- "L]};
    herald.length ← 0; Time.AppendCurrent[herald]; PutLine[s, herald]};

  RepeatCommand: PROC [s: Stream.Handle] ~ {
    OPEN CharIO;
    PutString[s, "\nPackaging "L];
    PutString[s, gd.sourceBcdName];
    PutString[s, " according to "L];
    PutString[s, gd.packName];
    PutString[s, "\nOutput to "L];
    PutString[s, gd.outputBcdName];
    PutChar[s, '\n];
    IF listPacks THEN {
      PutString[s, "Code and frame pack listing to "L];
      PutString[s, gd.packListFileName]; PutChar[s, '\n]};
    IF gd.printMap THEN {
      PutString[s, "Code and frame pack map to "L];
      PutString[s, gd.mapFileName]; PutChar[s, '\n]};
    PutChar[s, '\n]};


  -- Error logging

  errorFile: CIFS.OpenFile ← NIL;
  errorStream: Stream.Handle ← NIL;
  errorStreamObject: Stream.Object;

  OpenErrorStream: PROC ~ {
    errorName: Strings.String ← CommandUtil.CopyString[
      gd.rootName, 1+("errlog"L).length];
    errorName ← CommandUtil.SetExtension[errorName, "errlog"L];
    errorFile ← GetFile[errorName, $write];
    errorStream ← FileStream.Create[errorFile.GetFC];
    FileStream.SetLength[errorStream, 0];
    WriteHerald[errorStream, errorName];
    RepeatCommand[errorStream];
    errorName ← CommandUtil.FreeString[errorName]};

  CloseErrorStream: PROC ~ {
    IF errorStream # NIL THEN {errorStream.Delete[]; errorStream ← NIL};
    IF errorFile # NIL THEN {CIFS.Close[errorFile]; errorFile ← NIL}};

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

  ErrorPutBlock: Stream.PutProcedure
    --[sH: Handle, block: Block, endPhysicalRecord: BOOL]--  ~ {
    IF errorStream = NIL THEN OpenErrorStream[];
    errorStream.PutBlock[block, endPhysicalRecord]};

  ErrorDestroy: Stream.DeleteProcedure --[sH: Stream.Handle]--  ~ {
    CloseErrorStream[]};

  Logger: PROC [proc: PROC [log: Stream.Handle]] = {
    proc[gd.errorStream]};
 
 
  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[gd.rootName, 1+extension.length]
       ELSE fileName);
    RETURN [CommandUtil.SetExtension[root, extension]]};

  TimeSince: PROC [start: LONG CARDINAL] RETURNS [elapsedTime: LONG CARDINAL] ~ {
    RETURN [Time.Current[] - start]};


  
  -- #### THIS MAIN PROCEDURE IS CALLED TO DO PACKAGING ####

  globalData: PUBLIC PackagerDefs.GlobalData ← @gd;
  gd: PackagerDefs.GlobalDataRecord;  -- local copy of globalData

  DoPackaging: ENTRY PROC [cmd: ExecOps.Command] RETURNS [ExecOps.Outcome] ~ {
    ENABLE {
      CIFS.Error => TRUSTED {
--	name: Strings.String ← "unknown";
--	LogString["Problem in accessing file: "L];
--	LogLine[name];
        LogRope[error]; LogChar['\n];
        GOTO FileProblem};
      UNWIND => {NULL}};
    RETURN [DoPackagingInternal[cmd]];
    EXITS
      FileProblem => RETURN [$errors];
    };

  DoPackagingInternal: PROC [cmd: ExecOps.Command] RETURNS [ExecOps.Outcome] ~ {
    theCommand: CommandUtil.CommandObject ← [pos~0, len~0, data~cmd];
    key, value: Strings.String;
    abortRequested: BOOL ← FALSE;
    globalErrors, globalWarnings: BOOL ← FALSE;
    parsed, aborted: BOOL;
    packagerStartTime: LONG CARDINAL;

    Initialize: PROC ~ {
      IF gd.ownTable = NIL THEN {
        weights: ARRAY [0..PackagerDefs.packagerNTables) OF Alloc.TableInfo ← [
	  [16, FALSE[32]], [2, FALSE[8]], [4, FALSE[12]], [1, FALSE[1]],
	  [2, FALSE[8]], [1, FALSE[1]], [4, FALSE[8]], [1, FALSE[2]],
	  [4, FALSE[12]], [1, FALSE[1]], [1, FALSE[1]], [1, FALSE[1]],
	  [1, FALSE[1]], [1, FALSE[1]], [1, FALSE[1]], [1, FALSE[1]],
	  [1, FALSE[1]], [12, FALSE[24]], [1, FALSE[1]], [1, FALSE[1]],
	  [8, FALSE[12]], [12, FALSE[24]], [12, FALSE[24]], [4, FALSE[8]],
	  [4, FALSE[8]], [12, FALSE[24]]];
        gd.ownTable ← Alloc.Create[weights~DESCRIPTOR[weights]];
        (gd.ownTable).Chunkify[table~Alloc.defaultChunkType]}
      ELSE (gd.ownTable).Reset[];
      IF gd.debug THEN PackDebug.Initialize[];
      TreeOps.Initialize[gd.ownTable, gd.zone];
      HashOps.Initialize[gd.ownTable, PackagerDefs.packhttype, PackagerDefs.packsstype];
      gd.errors ← gd.warnings ← aborted ← FALSE;
      gd.nErrors ← gd.nWarnings ← 0;
      gd.textIndex ← PackagerDefs.nullSourceIndex};

    Finalize: PROC ~ {
      HashOps.Finalize[];
      TreeOps.Finalize[];
      IF gd.debug THEN PackDebug.Finalize[];
      IF abortRequested THEN LogLine["Packaging aborted"L]
      ELSE IF gd.errors THEN LogLine["Errors detected; Bcd not written"L]
      ELSE {
        LogDecimal[Inline.LowHalf[TimeSince[packagerStartTime]]];
        LogLine[" seconds"L]};
      IF errorStream # NIL THEN {
        LogString["See "L]; LogString[gd.rootName]; LogLine[".errlog"L]};
      CloseErrorStream[];
      (gd.ownTable).Reset[]};

    packFile, packListFile, mapFile: CIFS.OpenFile ← NIL;

    OpenFiles: PROC ~ {
      packFile ← GetFile[gd.packName, $read];
      gd.sourceBcdFile ← GetFile[gd.sourceBcdName, $read];
      gd.outputBcdFile ← GetFile[gd.outputBcdName, $write];
      IF listPacks THEN packListFile ← GetFile[gd.packListFileName, $write];
      IF gd.printMap THEN mapFile ← GetFile[gd.mapFileName, $write]};

    CloseFiles: PROC ~ {
      IF gd.packStream # NIL THEN gd.packStream.Delete[];
      IF packFile # NIL THEN {CIFS.Close[packFile]; packFile ← NIL};
      IF gd.packListStream # NIL THEN gd.packListStream.Delete[];
      IF packListFile # NIL THEN {CIFS.Close[packListFile]; packListFile ← NIL};
      IF gd.mapStream # NIL THEN gd.mapStream.Delete[];
      IF mapFile # NIL THEN {CIFS.Close[mapFile]; mapFile ← NIL};
      IF gd.outputBcdFile # NIL THEN {
        IF gd.nErrors # 0 THEN {
--        MFile.SetAccess[
--          gd.outputBcdFile, delete ! MFile.Error => GOTO cantDelete];
--        MFile.Delete[gd.outputBcdFile ! MFile.Error => CONTINUE];
	  GOTO cantDelete
	  EXITS cantDelete => NULL}
        ELSE CIFS.Close[gd.outputBcdFile];
        gd.outputBcdFile ← NIL};
      IF gd.sourceBcdFile # NIL THEN {
        CIFS.Close[gd.sourceBcdFile];
        gd.sourceBcdFile ← NIL}};

    IF cmd = NIL THEN ERROR;
    WHILE theCommand.data[theCommand.len] # '\n DO
      theCommand.len ← theCommand.len + 1;
      ENDLOOP;
  
    gd.zone ← NIL;  gd.ownTable ← NIL;
    globalPause ← TRUE;
    listPacks ← FALSE;

    gd.packagerVersion.time ← Runtime.GetBcdTime[];
    gd.packagerVersion.net ← gd.packagerVersion.host ← 0;
    [gd.network, gd.host] ← GetNetAndHost[];

    BEGIN
    ENABLE {
      UNWIND => {
        IF gd.ownTable # NIL THEN {
          Alloc.Destroy[gd.ownTable]; gd.ownTable ← NIL};
        IF gd.zone # NIL THEN {Heap.Delete[gd.zone]; gd.zone ← NIL};
        CloseFiles[]};
      Alloc.Overflow => {RESUME [8]}};
    OpenLogStream[];
    WriteHerald[log];
    DO  -- until no more Packager commands
      BEGIN
      configTreeRoot: SourceBcd.CTreeIndex ← SourceBcd.nullCTreeIndex;
      
      IF gd.zone = NIL THEN {
        gd.zone ← Heap.Create[initial~50, increment~10, swapUnit~10];
        gd.rootName ← gd.zone.NEW[StringBody[100]]};
      gd.packName ← gd.sourceBcdName ← NIL;
      gd.outputBcdName ← gd.packListFileName ← NIL;
      gd.mapFileName ← NIL;
      gd.debug ← FALSE;  debugPass ← CARDINAL.LAST;
      gd.printMap ← listPacks ← FALSE;
      localPause ← FALSE;

      [gd.packName, args, results, switches] ← CommandUtil.Parse[
		s~@theCommand,
		opX~1+("pack"L).length, resultX~1+("list"L).length
	  ! CommandUtil.Failed => {GO TO BadCommandLineSyntax}];
      IF gd.packName = NIL AND switches = NIL THEN EXIT;  -- done packaging
      IF gd.packName = NIL THEN GO TO GlobalSwitches;

      SetRoot[gd.rootName, gd.packName];

      FOR i: CARDINAL IN [0..results.ListLength) DO
        [key, value] ← results.GetNthPair[n~i];
        SELECT TRUE FROM
          (key = NIL), EquivalentString[key, "output"L] =>
            gd.outputBcdName ← CommandUtil.CopyString[
              s~value, extra~(".bcd"L).length];
          EquivalentString[key, "list"L] => {
            listPacks ← TRUE;
            gd.packListFileName ← CommandUtil.CopyString[
              s~value, extra~(".list"L).length]};
          EquivalentString[key, "map"L] => {
            gd.printMap ← TRUE;
            gd.mapFileName ← CommandUtil.CopyString[
              s~value, extra~(".map"L).length]};
          ENDCASE => GO TO BadCommandLineSemantics;
        ENDLOOP;

      FOR i: CARDINAL IN [0..args.ListLength) DO
        [key, value] ← args.GetNthPair[n~i];
        SELECT TRUE FROM
          (key = NIL), EquivalentString[key, "input"L] =>
            gd.sourceBcdName ← CommandUtil.CopyString[
              s~value, extra~(".bcd"L).length];
          ENDCASE => GO TO BadCommandLineSemantics;
        ENDLOOP;

      IF switches # NIL THEN {
        i: CARDINAL ← 0;
        sense: BOOL ← TRUE;
        WHILE i < switches.length DO
          c: CHAR ~ switches[i];
          SELECT c FROM
            '-, '~ => sense ← ~sense;
            'l, 'L => {listPacks ← sense; sense ← TRUE};
            'm, 'M => {gd.printMap ← sense; sense ← TRUE};
            'p, 'P => {localPause ← sense; sense ← TRUE};
            'b, 'B => sense ← TRUE;  -- no longer necessary
            'd, 'D => {gd.debug ← sense; sense ← TRUE};
            IN ['0..'5] => {debugPass ← c.ORD-'0.ORD; sense ← TRUE};
            ENDCASE;
          i ← i + 1;
          ENDLOOP;
        switches ← CommandUtil.FreeString[switches]};

      IF gd.sourceBcdName = NIL THEN GOTO NoSourceBcdFileGiven;
      gd.packName ← SetFileName[gd.packName, "pack"L];
      gd.sourceBcdName ← SetFileName[gd.sourceBcdName, "bcd"L];
      gd.outputBcdName ← SetFileName[gd.outputBcdName, "bcd"L];
      IF listPacks THEN
        gd.packListFileName ← SetFileName[gd.packListFileName, "list"L];
      IF gd.printMap THEN gd.mapFileName ← SetFileName[gd.mapFileName, "map"L];

      RepeatCommand[log];
      packagerStartTime ← Time.Current[];

      gd.sourceBcdFile ← gd.outputBcdFile ← NIL;
      gd.packStream ← gd.packListStream ← gd.mapStream ← NIL;
      OpenFiles[ ! CIFS.Error => TRUSTED {CONTINUE}];

      IF packFile = NIL THEN GO TO CantFindPackFile
      ELSE {
        gd.packStream ← FileStream.Create[packFile.GetFC];
        gd.packVersion ← BcdDefs.VersionStamp[
          net~0, host~0, time~FileStream.GetLeaderProperties[gd.packStream].create];
        packFile ← NIL};

      IF gd.sourceBcdFile = NIL THEN GO TO CantFindSourceBcdFile;
      IF gd.outputBcdFile = NIL THEN GO TO CantOpenObjectBcdFile;

      IF listPacks THEN {
        IF packListFile = NIL THEN packListFile ← GetFile[gd.packListFileName, $write];
        gd.packListStream ← FileStream.Create[packListFile.GetFC]};

      IF gd.printMap THEN {
        IF mapFile = NIL THEN mapFile ← GetFile[gd.mapFileName, $write];
        gd.mapStream ← FileStream.Create[mapFile.GetFC]};

      gd.logStream ← NIL;
      errorStream ← NIL;
      errorStreamObject ← [  -- install private putByte and destroy procedures 
        options~TRASH, 
        getByte~TRASH, putByte~ErrorPut, 
        getWord~TRASH, putWord~TRASH,
        get~TRASH, put~ErrorPutBlock, 
        setSST~TRASH, sendAttention~TRASH, waitAttention~TRASH, 
        delete~ErrorDestroy];
      gd.errorStream ← @errorStreamObject;
      Initialize[];

      BEGIN
      ENABLE {
        Alloc.Failure => {
          gd.errors ← TRUE;
          IF ~gd.debug THEN {LogLine["Storage Overflow"L]; GOTO StorageOverflow}};
        UNWIND => Finalize[]};

      IF ExecOps.CheckForAbort[] THEN GO TO Abort;
      SourceBcd.Load[ ! SourceBcd.BadSourceBcd => {GO TO InvalidSourceBcd}];
      LogString["Parsing"L];
      [complete~parsed, nErrors~gd.nErrors] ← P1.Parse[gd.packStream, gd.zone, Logger];
      IF gd.nErrors # 0 THEN gd.errors ← TRUE;
      IF ~parsed THEN GO TO ParseFailed;
      IF debugPass <= 1 THEN PackDebug.PrintTree[];
      IF ExecOps.CheckForAbort[] THEN GO TO Abort;
      IF ~gd.errors THEN {
        gd.root ← TreeOps.PopTree[];
        LogString["...Building semantic entries"L];
        configTreeRoot ← SourceBcd.BuildConfigTree[];
        SemanticEntry.BuildSemanticEntries[];
        IF debugPass <= 2 THEN {
          PackDebug.PrintSourceBcd[];
          PackDebug.PrintConfigTree[configTreeRoot];
          PackDebug.PrintTree[]};
        IF ExecOps.CheckForAbort[] THEN GO TO Abort;
        IF ~gd.errors THEN {
          LogString["...Determining packs"L];
          ProcessingOrder.Determine[];
          IF debugPass <= 3 THEN PackDebug.PrintProcessingOrder[configTreeRoot];
          IF ExecOps.CheckForAbort[] THEN GO TO Abort;
          ModuleSymbols.Initialize[SourceBcd.moduleCount];
          IF ~gd.errors THEN {
            CodePackProcs.Determine[configTreeRoot];
            IF ExecOps.CheckForAbort[] THEN GO TO Abort;
            IF ~gd.errors THEN {
              IF listPacks THEN PackList.Print[];
              LogString["...Computing code placement"L];
              PackCode.ComputeCodePlacement[
                ! PackCode.PackError => {IF reason=$SegmentTooBig THEN GO TO dont}];
              IF ExecOps.CheckForAbort[] THEN GO TO Abort;
              LogString["...Writing Bcd"L];
              PackCode.WriteBcdToFile[];
              PackCode.WriteCodeToBcdFile[];
              EXITS dont => PackCode.Finalize[]}}}};
      EXITS
        StorageOverflow => NULL;
        ParseFailed => gd.errors ← aborted ← TRUE;
        InvalidSourceBcd => gd.errors ← aborted ← TRUE;
        Abort => abortRequested ← TRUE;
      END;
      CodePackProcs.Destroy[];
      ProcessingOrder.Destroy[];
      ModuleSymbols.Finalize[];
      SourceBcd.DestroyConfigTree[configTreeRoot];
      SourceBcd.Unload[];
      LogChar['\n];
      Finalize[];
      EXITS
        NoSourceBcdFileGiven => {
          LogLine["\nNo source BCD file given\n"L];
          gd.errors ← TRUE};
        CantFindPackFile => {
          LogString["\nCan't find pack file "L];
          LogLine[gd.packName];
          gd.errors ← TRUE};
        CantFindSourceBcdFile => {
          LogString["\nCan't find source Bcd "L];
          LogLine[gd.sourceBcdName];
          gd.errors ← TRUE};
        CantOpenObjectBcdFile => {
          LogString["\nCan't open output Bcd "L];
          LogLine[gd.outputBcdName];
          gd.errors ← TRUE};
        GlobalSwitches => {
          sense: BOOL ← TRUE;
          FOR i: CARDINAL IN [0..switches.length) DO
            c: CHAR ~ switches[i];
            SELECT c FROM
              '-, '~ => sense ← ~sense;
              'b, 'B => sense ← TRUE;  -- ignored
              'p, 'P => {globalPause ← sense; sense ← TRUE};
              ENDCASE => EXIT;
            ENDLOOP;
          switches ← CommandUtil.FreeString[switches]};
        BadCommandLineSemantics => {
          LogLine[" -- Illegal Packager command"L];
          gd.errors ← TRUE};
      END;

      CloseFiles[];
      gd.packName ← CommandUtil.FreeString[gd.packName];
      gd.sourceBcdName ← CommandUtil.FreeString[gd.sourceBcdName];
      gd.outputBcdName ← CommandUtil.FreeString[gd.outputBcdName];
      IF listPacks THEN
        gd.packListFileName ← CommandUtil.FreeString[gd.packListFileName];
      IF gd.printMap THEN gd.mapFileName ← CommandUtil.FreeString[gd.mapFileName];
      results ← CommandUtil.FreePairList[results];
      args ← CommandUtil.FreePairList[args];
      IF gd.errors THEN globalErrors ← TRUE;
      IF gd.warnings THEN globalWarnings ← TRUE;
      IF abortRequested THEN EXIT;  -- stop Packager
      IF gd.errors OR gd.warnings THEN {
        IF localPause THEN {globalPause ← TRUE; EXIT}};
      REPEAT
        BadCommandLineSyntax => {
          LogLine["\n-- Illegal Packager command syntax"L];
          gd.errors ← globalErrors ← TRUE};
      ENDLOOP;

    END;
    IF gd.ownTable # NIL THEN {Alloc.Destroy[gd.ownTable]; gd.ownTable ← NIL};
    IF gd.zone # NIL THEN {Heap.Delete[gd.zone]; gd.zone ← NIL};
    CloseLogStream[];
    RETURN [SELECT TRUE FROM
         abortRequested => $aborted,
         globalErrors => $errors,
         globalWarnings => $warnings,
         ENDCASE => $ok];
    };  -- of procedure DoPackagingInternal


  -- MAIN BODY CODE

  CallPackager: PROC ~ {
    nChars: CARDINAL ~ (Exec.commandLine.s.length-Exec.commandLine.i) + 1;
    command: ExecOps.Command ← Heap.MakeNode[n~(nChars+1)/2];
    j: CARDINAL ← 0;
    FOR i: CARDINAL IN [Exec.commandLine.i..Exec.commandLine.s.length) DO
      command[j] ← Exec.commandLine.s[i];  j ← j+1;
      ENDLOOP;
    command[j] ← '\n;
    [] ← DoPackaging[command];
    Heap.FreeNode[p~command]};

  InitializeSelf: PROC ~ {
    P1.InstallParseTable[Runtime.GetTableBase[LOOPHOLE[PackParseData]]];
    Exec.AddCommand[name~"CrossPackager.~", proc~CallPackager]};

  InitializeSelf[];

  }.