-- PackControl.Mesa  
-- Last edited by Lewis on  3-Apr-81 10:02:05
-- Last edited by Sweet on  6-Feb-81 14:28:01
-- Last edited by Levin on July 6, 1982 4:16 pm

DIRECTORY
  Alloc USING [
    Chunkify, Create, defaultChunkType, Destroy, Failure,
    Overflow, Reset, TableInfo],
  BcdDefs USING [VersionStamp],
  CharIO USING [CR, PutChar, PutString, SP],
  CodePackProcs USING [Determine, Destroy],
  CommandUtil USING [
    CommandObject, CommandPtr, CopyString, Failed, FreePairList, FreeString, 
    GetNthPair, ListLength, PairList, Parse, SetExtension],
  Debug USING [
    Initialize, Finalize, PrintConfigTree, PrintSourceBcd, PrintTree,
    PrintProcessingOrder],
  Exec USING [AddCommand, commandLine, CommandLine, w],
  Inline USING [BITOR, BITXOR, HighByte, LowByte, LowHalf],
  LongStorage USING [Node, String],
  ModuleSymbols USING [Initialize, Finalize],
  PackagerDefs USING [NullSourceIndex, PackagerDataRecord, PackagerNTables],
  PackCode USING [ComputeCodePlacement, PackError, WriteBcdToFile, WriteCodeToBcdFile],
  PackEnviron USING [Zero],
  PackHeap USING [Initialize, Finalize],
  ParseData,
  P1: FROM "PackParseDefs" USING [Parse],
  ProcessingOrder USING [Determine, Destroy],
  ProcessorFace USING [processorID],
  PackList USING [Print],
  Runtime USING [CallDebugger, GetBcdTime, GetTableBase], 
  Segments USING [
    Access, Address, DefaultVersion, DestroyFile, FHandle, FileNameProblem, 
    FileProblem, LockFile, NewFile, PageCount, Read, ReleaseFile, 
    UnlockFile, Write],
  SemanticEntry USING [BuildSemanticEntries],
  SourceBcd USING [
    BuildConfigTree, DestroyConfigTree, Load, Unload, BadSourceBcd,
    moduleCount],
  Space USING [
    Create, Delete, GetHandle, Handle, LongPointer, Map, PageFromLongPointer, 
    virtualMemory], 
  Stream USING [DeleteProcedure, Object, PutByteProcedure],
  Streams USING [CreateStream, Destroy, GetTimes, Handle, NewStream, PutByte],
  Strings USING [
    AppendChar, AppendString, EquivalentSubString, String, SubStringDescriptor],
  SymTabOps USING [Initialize, Finalize],
  Time USING [Append, AppendCurrent, Current, Unpack],
  TreeOps: FROM "PackTreeOps" USING [Initialize, Finalize],
  TTY USING [PutChar, PutCR, PutDecimal, PutLongString, UserAbort],
  UserTerminal USING [CursorArray, GetCursorPattern, SetCursorPattern];

PackControl: PROGRAM
  IMPORTS
    Alloc, CharIO, CommandUtil, CodePackProcs, Debug, Exec, Inline, LongStorage, 
    ModuleSymbols, PackCode, PackEnviron, PackHeap, ParseData, P1,
    ProcessingOrder, ProcessorFace, PackList, Runtime, Segments, SemanticEntry, 
    SourceBcd, Space, Streams, Strings, SymTabOps, Time, TreeOps, TTY, UserTerminal  
  EXPORTS
    PackagerDefs
  SHARES
    ProcessorFace =
  BEGIN

  CR: CHARACTER = CharIO.CR;
  SP: CHARACTER = CharIO.SP;
  

  EquivalentString: PROC [s1, s2: Strings.String] RETURNS [BOOLEAN] =
    BEGIN
    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] ];
    END;

  GetNetAndHost: PROC RETURNS [net, host: CARDINAL] = 
    BEGIN
    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]];
    END;


 -- Cursor control

  savedCursor: UserTerminal.CursorArray;

  PackagerCursor: UserTerminal.CursorArray =  -- package
   [1670B, 1110B, 520B, 7776B, 14102B, 24102B, 47776B, 54102B, 
   64102B, 47776B, 50204B, 60410B, 77760B, 0, 0, 0];

  SaveOriginalCursor: PROC =
    BEGIN
    savedCursor ← UserTerminal.GetCursorPattern[];  
    END;

  DisplayPackagerCursor: PROC [pass: CARDINAL] =
    BEGIN
    cursor: UserTerminal.CursorArray ← PackagerCursor;
    box: CARDINAL ← 60000B;
    THROUGH [0..pass) DO
      cursor[14] ← Inline.BITOR[cursor[14], box];
      cursor[15] ← Inline.BITOR[cursor[15], box];
      box ← box/8;
      ENDLOOP;  
    UserTerminal.SetCursorPattern[cursor];
    END;

  RestoreOriginalCursor: PROC =
    {UserTerminal.SetCursorPattern[savedCursor]};


 -- Table storage management
  
  tableSpace: Segments.Address ← NIL;

  InitializeTables: PROC =
    BEGIN
    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]]];
    IF globalData.ownTable = NIL THEN {
      globalData.ownTable ← Alloc.Create[weights: DESCRIPTOR[weights]];
      Alloc.Chunkify[h: globalData.ownTable, table: Alloc.defaultChunkType]}
    ELSE Alloc.Reset[globalData.ownTable];
    END;

  ReleaseTables: PROC =
    BEGIN
    Alloc.Reset[globalData.ownTable];
    END;

  GetStorage: PROC [
      pages: Segments.PageCount] RETURNS [base: Segments.Address] =
    BEGIN
    new: Space.Handle ← Space.Create[
      size: pages, parent: Space.virtualMemory];
    Space.Map[new];
    base ← Space.LongPointer[new];
    END;

  FreeStorage: PROC [base: Segments.Address] = 
    {Space.Delete[Space.GetHandle[Space.PageFromLongPointer[base]]]};
  

 -- Packager log

  LogString: PROC [s: Strings.String] = {TTY.PutLongString[Exec.w, s]};
  LogLine: PROC [s: Strings.String] = {LogString[s]; TTY.PutCR[Exec.w]};
  LogChar: PROC [c: CHARACTER] = {TTY.PutChar[Exec.w, c]};
  NewLine: PROC = INLINE {TTY.PutCR[Exec.w]};
  LogDecimal: PROC [i: CARDINAL] = {TTY.PutDecimal[Exec.w, i]};
  
  LogHerald: PROC =
    BEGIN
    t: STRING ← [20];
    LogString["Cedar/Mesa Packager 7.0c of "L];
    Time.Append[t, Time.Unpack[LOOPHOLE[globalData.packagerVersion.time]]];
    t.length ← t.length - 3;
    LogLine[t];
    t.length ← 0;  Time.AppendCurrent[t];  t.length ← t.length - 3;
    LogLine[t];
    END;

  LogCommand: PROC =
    BEGIN OPEN gd: globalData;
    NewLine[];
    LogString["Packaging "L];  LogString[gd.sourceBcdName];
    LogString[" according to "L];  LogLine[gd.packName];
    LogString["Output to "L];  LogLine[gd.outputBcdName];
    IF listPacks THEN
      BEGIN
      LogString["Code and frame pack listing to "L];  
      LogLine[gd.packListFileName];
      END;
    IF gd.printMap THEN
      BEGIN
      LogString["Code and frame pack map to "L];  
      LogLine[gd.mapFileName];
      END;
    END;


 -- Command gathering

  args, results: CommandUtil.PairList;
  switches: Strings.String;
  
  globalPause, localPause, listPacks: BOOLEAN;
  debugPass: CARDINAL;

  execLine: Exec.CommandLine;
  cmdObject: CommandUtil.CommandObject;
  cmdChars: PACKED ARRAY [0..200) OF CHARACTER;

  GetPackagingCommands: PROC RETURNS [cmd: CommandUtil.CommandPtr] = 
    BEGIN
    c: CHARACTER;
    startFound: BOOLEAN ← FALSE;
    execLine ← Exec.commandLine;
    cmdObject ← CommandUtil.CommandObject[pos: 0, len: 0, data: @cmdChars];
    FOR j: CARDINAL IN [execLine.i .. execLine.s.length) DO
      c ← execLine.s[j];
      SELECT c FROM
        CR => EXIT;
	SP =>  -- ignore initial blanks 
	  IF startFound THEN 
	    {cmdChars[cmdObject.len] ← c;  cmdObject.len ← cmdObject.len + 1};
	ENDCASE => 
	  BEGIN
	  startFound ← TRUE;
	  cmdChars[cmdObject.len] ← c;  cmdObject.len ← cmdObject.len + 1;
	  END;
      ENDLOOP;
    RETURN[@cmdObject];
    END;
    

 -- Error logging

  errorFile: Segments.FHandle ← NIL;
  errorStreamObject: Stream.Object;
  errorStream: Streams.Handle ← NIL;

  OpenErrorStream: PROC =
    BEGIN
    errorName: Strings.String ← CommandUtil.CopyString[
      globalData.rootName, 2+("errlog"L).length];
    nameCopy: STRING ← [40];
    errorName ← CommandUtil.SetExtension[errorName, "errlog"L];
    IF errorFile = NIL THEN {
      nameCopy.length ← 0;
      Strings.AppendString[to: nameCopy, from: errorName];
      errorFile ← Segments.NewFile[nameCopy, Segments.Write];
      Segments.LockFile[errorFile]};
    errorStream ← Streams.CreateStream[errorFile, Segments.Write];
    WriteHeraldToErrlog[errorName];  WriteCommandToErrlog[];
    errorName ← CommandUtil.FreeString[errorName];
    END;
  
  WriteHeraldToErrlog: PROC [errorFileName: Strings.String] =
    BEGIN
    t: STRING ← [20];
    CharIO.PutString[errorStream, "Cedar/Mesa Packager 7.0c of "L];
    Time.Append[t, Time.Unpack[LOOPHOLE[globalData.packagerVersion.time]]];
    t.length ← t.length - 3;
    CharIO.PutString[errorStream, t];  CharIO.PutChar[errorStream, CR];
    t.length ← 0;
    Time.AppendCurrent[t];
    t.length ← t.length - 3;
    CharIO.PutString[errorStream, errorFileName];  
    CharIO.PutString[errorStream, " -- "L];
    CharIO.PutString[errorStream, t];  CharIO.PutChar[errorStream, CR];
    END;

  WriteCommandToErrlog: PROC =
    BEGIN OPEN gd: globalData;
    CharIO.PutChar[errorStream, CR];
    CharIO.PutString[errorStream, "Packaging "L];  CharIO.PutString[errorStream, gd.sourceBcdName];
    CharIO.PutString[errorStream, " according to "L];  CharIO.PutString[errorStream, gd.packName];
    CharIO.PutChar[errorStream, CR];
    CharIO.PutString[errorStream, "Output to "L];  CharIO.PutString[errorStream, gd.outputBcdName];
    CharIO.PutChar[errorStream, CR];
    IF listPacks THEN
      BEGIN
      CharIO.PutString[errorStream, "Code and frame pack listing to "L];  
      CharIO.PutString[errorStream, gd.packListFileName];
      CharIO.PutChar[errorStream, CR];
      END;
    IF gd.printMap THEN
      BEGIN
      CharIO.PutString[errorStream, "Code and frame pack map to "L];  
      CharIO.PutString[errorStream, gd.mapFileName];
      CharIO.PutChar[errorStream, CR];
      END;
    CharIO.PutChar[errorStream, CR];
    END;

  CloseErrorStream: PROC =
    BEGIN
    IF errorFile # NIL THEN 
      BEGIN
      Segments.UnlockFile[errorFile];
      SELECT TRUE FROM
        errorStream # NIL => {Streams.Destroy[errorStream];  errorStream ← NIL};
        errorFile # NIL   => Segments.DestroyFile[errorFile];
        ENDCASE;
      errorFile ← NIL;
      END;
    END;

  ErrorPut: Stream.PutByteProcedure --[sH: Stream.Handle, byte: Stream.Byte]-- =
    BEGIN
    IF errorStream = NIL THEN OpenErrorStream[];
    Streams.PutByte[errorStream, LOOPHOLE[byte, CARDINAL]];
    END;

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

  packFile, packListFile, mapFile: Segments.FHandle ← NIL;

  OpenFiles: PROC = 
    BEGIN OPEN gd: globalData;
    packFile ← OpenOneFile[gd.packName, Segments.Read];
    gd.sourceBcdFile ← OpenOneFile[gd.sourceBcdName, Segments.Read];
    gd.outputBcdFile ← OpenOneFile[gd.outputBcdName, Segments.Write];
    IF listPacks THEN
      packListFile ← OpenOneFile[gd.packListFileName, Segments.Write];
    IF gd.printMap THEN
      mapFile ← OpenOneFile[gd.mapFileName, Segments.Write];
    END;
    
  OpenOneFile: PROC [
      name: Strings.String, access: Segments.Access] RETURNS [file: Segments.FHandle] =
    BEGIN
    nameCopy: STRING ← [40];  
    Strings.AppendString[to: nameCopy, from: name];
    file ← NIL;
    file ← Segments.NewFile[nameCopy, access
      ! Segments.FileNameProblem[] => CONTINUE];
    IF file # NIL THEN Segments.LockFile[file];
    END;
    

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

  SetFileName: PROC [fileName, extension: Strings.String] RETURNS [Strings.String] =
    BEGIN
    root: Strings.String = IF fileName # NIL
      THEN fileName
      ELSE CommandUtil.CopyString[globalData.rootName, 2+extension.length];
    RETURN[CommandUtil.SetExtension[root, extension]];
    END;


  -- timing procedures
    
  startTime: LONG CARDINAL;

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

  MarkStartTime: PROC = {startTime ← Time.Current[]};

  PrintElapsedTime: PROC [legend: STRING] =
    BEGIN
    IF globalData.debug THEN
      BEGIN
      LogString[legend];  LogDecimal[Inline.LowHalf[TimeSince[startTime]]]; 
      LogLine[" seconds"L];
      END;
    END;


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

DoPackaging: PROC = 
  BEGIN
  packCommands: CommandUtil.CommandPtr;
  key, value: Strings.String;
  abortRequested: BOOLEAN ← FALSE;

  SaveOriginalCursor[];
  DisplayPackagerCursor[0];
  
  globalPause ← TRUE;
  listPacks ← FALSE;
  debugPass ← LAST[CARDINAL];

  packCommands ← GetPackagingCommands[];
  
  globalData.packagerVersion.time ← Runtime.GetBcdTime[];
  globalData.packagerVersion.net ← globalData.packagerVersion.host ← 0;
  [globalData.network, globalData.host] ← GetNetAndHost[];
  
  BEGIN
    ENABLE {
      UNWIND =>
        IF globalData.ownTable ~= NIL THEN {
          Alloc.Destroy[globalData.ownTable]; globalData.ownTable ← NIL};
      Alloc.Overflow => RESUME[8]};


 -- MAIN LOOP --

  LogHerald[];
  DO  -- until no more Packager commands
    BEGIN

    parsed, aborted: BOOLEAN;
    packagerStartTime: LONG CARDINAL;

    Initialize: PROC =
      BEGIN
      LogCommand[];
      globalData.errors ← globalData.warnings ← aborted ← FALSE;
      globalData.nErrors ← globalData.nWarnings ← 0;
      globalData.textIndex ← PackagerDefs.NullSourceIndex;
      PackHeap.Initialize[];
      errorStream ← NIL;
      END;

    Finalize: PROC =
      BEGIN
      SymTabOps.Finalize[];  TreeOps.Finalize[];  PackHeap.Finalize[];
      IF globalData.debug THEN Debug.Finalize[];
      IF abortRequested THEN LogLine["Packaging aborted"L]
      ELSE IF globalData.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[globalData.rootName];  LogLine[".errlog"L]};
      CloseErrorStream[];
      Streams.Destroy[globalData.packStream];
      IF globalData.packListStream # NIL THEN
        Streams.Destroy[globalData.packListStream];
      IF globalData.mapStream # NIL THEN Streams.Destroy[globalData.mapStream];
      IF globalData.outputBcdFile # NIL THEN 
        BEGIN
	Segments.UnlockFile[globalData.outputBcdFile];
        IF globalData.nErrors # 0 THEN Segments.DestroyFile[globalData.outputBcdFile
	    ! Segments.FileProblem[] => CONTINUE]
        ELSE Segments.ReleaseFile[globalData.outputBcdFile  
	    ! Segments.FileProblem[] => CONTINUE];
	globalData.outputBcdFile ← NIL;
	END;
      IF globalData.sourceBcdFile # NIL THEN 
        BEGIN
	Segments.UnlockFile[globalData.sourceBcdFile];
        Segments.ReleaseFile[globalData.sourceBcdFile  
	    ! Segments.FileProblem[] => CONTINUE];
	globalData.sourceBcdFile ← NIL;
	END;
      ReleaseTables[];
      END;


    globalData.rootName ← LongStorage.String[40];
    globalData.packName ← globalData.sourceBcdName ← NIL;
    globalData.outputBcdName ← globalData.packListFileName ← NIL;
    globalData.mapFileName ← NIL;
    globalData.debug ← FALSE;  debugPass ← LAST[CARDINAL];
    globalData.printMap ← listPacks ← FALSE;
    localPause ← FALSE;

    [globalData.packName, args, results, switches] ← CommandUtil.Parse[
		s: packCommands,
		opX: 2+("pack"L).length, resultX: 2+("list"L).length
	  ! CommandUtil.Failed => GO TO BadCommandLineSyntax];

    IF globalData.packName = NIL AND switches = NIL THEN EXIT;  -- done packaging

    IF globalData.packName = NIL THEN GO TO GlobalSwitches;
    SetRoot[globalData.rootName, globalData.packName];

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

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

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

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

    packagerStartTime ← Time.Current[];
    DisplayPackagerCursor[1];

    BEGIN
    ENABLE 
      ANY => CONTINUE;
    packFile ← errorFile ← packListFile ← mapFile ← NIL; 
    globalData.sourceBcdFile ← globalData.outputBcdFile ← NIL; 
    globalData.packStream ← globalData.packListStream ← NIL;
    globalData.mapStream ← NIL;
    OpenFiles[];
    IF packFile = NIL THEN 
      BEGIN
      IF globalData.outputBcdFile # NIL THEN 
        BEGIN
	Segments.UnlockFile[globalData.outputBcdFile];
        Segments.DestroyFile[globalData.outputBcdFile ! Segments.FileProblem[] => CONTINUE];
	END;
      GO TO CantFindPackFile;
      END
    ELSE 
      BEGIN
      globalData.packStream ← Streams.CreateStream[packFile, Segments.Read];
      globalData.packVersion ← BcdDefs.VersionStamp[
	net: 0, host: 0, time: Streams.GetTimes[globalData.packStream].create];
      Segments.UnlockFile[packFile];  packFile ← NIL;
      END;
    IF globalData.sourceBcdFile = NIL THEN GO TO CantFindSourceBcdFile;
    IF listPacks THEN
      BEGIN
      IF packListFile = NIL THEN
        BEGIN
	nameCopy: STRING ← [40];
	Strings.AppendString[to: nameCopy, from: globalData.packListFileName]; 
        globalData.packListStream ← Streams.NewStream[nameCopy, Segments.Write];
	END
      ELSE globalData.packListStream ← Streams.CreateStream[packListFile, Segments.Write];
      END;
    IF globalData.printMap THEN 
      BEGIN
      IF mapFile = NIL THEN
        BEGIN
	nameCopy: STRING ← [40];
	Strings.AppendString[to: nameCopy, from: globalData.mapFileName]; 
        globalData.mapStream ← Streams.NewStream[nameCopy, Segments.Write]
	END
      ELSE globalData.mapStream ← Streams.CreateStream[mapFile, Segments.Write];
      END;
    END;

    globalData.logStream ← NIL;
    errorFile ← NIL;
    errorStream ← NIL;
    errorStreamObject ←  -- install private putByte and destroy procedures 
      Stream.Object[
        options: NULL, getByte: NULL, 
	putByte: ErrorPut, 
	getWord: NULL, putWord: NULL, get: NULL, put: NULL,  
	setSST: NULL, sendAttention: NULL, waitAttention: NULL, 
	delete: ErrorDestroy];
    globalData.errorStream ← @errorStreamObject; 

    Initialize[];
      BEGIN 
          ENABLE
            BEGIN
            Alloc.Failure =>
              BEGIN
              globalData.errors ← TRUE;
              IF ~globalData.debug THEN
                BEGIN
                LogLine["Storage Overflow"L];
                GOTO StorageOverflow;
                END;
              END;
            UNWIND => Finalize[];
            END;
      IF TTY.UserAbort[] THEN GO TO AbortPackaging;
      InitializeTables[];
      SourceBcd.Load[ ! SourceBcd.BadSourceBcd => GO TO InvalidSourceBcd];  
      IF globalData.debug THEN Debug.Initialize[];
      SymTabOps.Initialize[];  TreeOps.Initialize[];
      MarkStartTime[];
      [complete: parsed, nErrors: globalData.nErrors] ← P1.Parse[
	  sourceStream:  globalData.packStream,
	  messageStream: globalData.errorStream,
	  table:         Runtime.GetTableBase[LOOPHOLE[ParseData]] ];
      IF globalData.nErrors # 0 THEN globalData.errors ← TRUE;
      PrintElapsedTime["Time to parse: "L];
      IF debugPass <= 1 THEN Debug.PrintTree[];
      IF ~parsed THEN GO TO ParseFailed;
      IF TTY.UserAbort[] THEN GO TO AbortAfterParsing;
      IF ~globalData.errors THEN 
        BEGIN
        MarkStartTime[];  DisplayPackagerCursor[2];
        SourceBcd.BuildConfigTree[];
        SemanticEntry.BuildSemanticEntries[];
        PrintElapsedTime["Time to decorate parse tree: "L];
        IF debugPass <= 2 THEN 
          {Debug.PrintSourceBcd[]; Debug.PrintConfigTree[]; Debug.PrintTree[]};
        IF TTY.UserAbort[] THEN GO TO AbortAfterBuildingSEs;
        IF ~globalData.errors THEN 
          BEGIN
          MarkStartTime[];  DisplayPackagerCursor[3];
          ProcessingOrder.Determine[];
          PrintElapsedTime["Time to determine processing order: "L];
          IF debugPass <= 3 THEN Debug.PrintProcessingOrder[];
	  IF TTY.UserAbort[] THEN GO TO AbortAfterDeterminingPO;
          ModuleSymbols.Initialize[SourceBcd.moduleCount];
          IF ~globalData.errors THEN 
            BEGIN
            MarkStartTime[];
  	    CodePackProcs.Determine[];
            PrintElapsedTime["Time to determine code pack procedures: "L];
	    IF TTY.UserAbort[] THEN GO TO AbortAfterDeterminingCPProcs;
            IF ~globalData.errors THEN 
              BEGIN
  	      IF listPacks THEN PackList.Print[];
              MarkStartTime[];  DisplayPackagerCursor[4];
	      PackCode.ComputeCodePlacement[ 
	        ! PackCode.PackError => 
		    IF reason = SegmentTooBig THEN GO TO dont];
              PrintElapsedTime["Time to compute code placement: "L];
	      IF TTY.UserAbort[] THEN GO TO AbortAfterComputingCodePlacement;
              MarkStartTime[];  DisplayPackagerCursor[5];
	      PackCode.WriteBcdToFile[];  PackCode.WriteCodeToBcdFile[];
              PrintElapsedTime["Time to create Bcd and copy code: "L];
	      EXITS
	        dont => NULL;
	      END;
  	    CodePackProcs.Destroy[];
	    END;
          ModuleSymbols.Finalize[];
          ProcessingOrder.Destroy[];
	  END;
        SourceBcd.DestroyConfigTree[];  
	END;
      SourceBcd.Unload[];
      EXITS
        StorageOverflow   => NULL;
        ParseFailed       => globalData.errors ← aborted ← TRUE;
        InvalidSourceBcd  => globalData.errors ← aborted ← TRUE;
        AbortPackaging => abortRequested ← TRUE;
        AbortAfterParsing => 
	  {SourceBcd.Unload[];  abortRequested ← TRUE};
	AbortAfterBuildingSEs => 
	  BEGIN
	  SourceBcd.DestroyConfigTree[];  SourceBcd.Unload[];  
	  abortRequested ← TRUE;
	  END;
	AbortAfterDeterminingPO =>
	  BEGIN
	  ProcessingOrder.Destroy[];
	  SourceBcd.DestroyConfigTree[];  SourceBcd.Unload[];  
	  abortRequested ← TRUE;
	  END;
	AbortAfterDeterminingCPProcs, AbortAfterComputingCodePlacement =>
	  BEGIN
	  CodePackProcs.Destroy[];  ModuleSymbols.Finalize[];  
	  ProcessingOrder.Destroy[];  
	  SourceBcd.DestroyConfigTree[];  SourceBcd.Unload[];  
	  abortRequested ← TRUE;
	  END;
      END;
    Finalize[];
    EXITS
      NoSourceBcdFileGiven =>
        BEGIN 
        LogChar[CR];
        LogString["No source BCD file given"L];  LogChar[CR];
        globalData.errors ← TRUE;
        END;
      CantFindPackFile =>
        BEGIN
        LogChar[CR];
        LogString["Can't find "L];  LogString[globalData.packName];
        LogChar[CR];
        globalData.errors ← TRUE;
        END;
      CantFindSourceBcdFile =>
        BEGIN
        LogChar[CR];
        LogString["Can't find "L];  LogString[globalData.sourceBcdName];
        LogChar[CR];
        globalData.errors ← TRUE;
        END;
      GlobalSwitches => 
        BEGIN
	sense: BOOLEAN;
        results ← CommandUtil.FreePairList[results];
        args ← CommandUtil.FreePairList[args];
        sense ← TRUE;
        FOR i: CARDINAL IN [0..switches.length) DO
	  c: CHARACTER = switches[i];
	  SELECT c FROM
	    '-, '~ => sense ← ~sense;
	    '!     => Runtime.CallDebugger[NIL];
            'b, 'B => sense ← TRUE;  -- ignored
	    'p, 'P => {globalPause ← sense;  sense ← TRUE};
	    ENDCASE => EXIT;
	  ENDLOOP;
        switches ← CommandUtil.FreeString[switches];
	END;
      BadCommandLineSemantics =>  
        BEGIN
        results ← CommandUtil.FreePairList[results];
        args ← CommandUtil.FreePairList[args];
        LogString[" -- Illegal Packager command"L];
        globalData.errors ← TRUE;
	END;
    END;
    globalData.packName ← CommandUtil.FreeString[globalData.packName];
    globalData.sourceBcdName ← CommandUtil.FreeString[globalData.sourceBcdName];
    globalData.outputBcdName ← CommandUtil.FreeString[globalData.outputBcdName];
    IF listPacks THEN
      globalData.packListFileName ← CommandUtil.FreeString[globalData.packListFileName];
    IF globalData.printMap THEN
      globalData.mapFileName ← CommandUtil.FreeString[globalData.mapFileName];
    results ← CommandUtil.FreePairList[results];
    args ← CommandUtil.FreePairList[args];
    IF globalData.errors OR globalData.warnings THEN
      BEGIN
      IF localPause THEN {globalPause ← TRUE;  EXIT};
      END;
    REPEAT
      BadCommandLineSyntax => 
        BEGIN
	NewLine[]; 
        LogString["-- Illegal Packager command syntax"L];  
        globalData.errors ← TRUE;
	END;
    ENDLOOP;
  END;

  IF globalData.ownTable ~= NIL THEN {
    Alloc.Destroy[globalData.ownTable]; globalData.ownTable ← NIL};
  RestoreOriginalCursor[];
  END;  -- of procedure DoPackaging
  
    
 -- MAIN BODY CODE

  globalData: PUBLIC LONG POINTER TO PackagerDefs.PackagerDataRecord;
  
  InitializeSelf: PROC =
    BEGIN
    globalData ← LongStorage.Node[SIZE[PackagerDefs.PackagerDataRecord]];
    PackEnviron.Zero[globalData, SIZE[PackagerDefs.PackagerDataRecord]];
    Exec.AddCommand[name: "Packager.~"L, proc: DoPackaging];
    END;
    
  InitializeSelf[];

  END.