-- 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[]; }.