-- file CrossTableControl.mesa
-- last edited by Satterthwaite on January 12, 1983 9:14 am

DIRECTORY
  BcdDefs: TYPE USING [VersionStamp],
  CharIO: TYPE USING [PutChar, PutDecimal, PutString],
  Exec: TYPE USING [CommandLine, AddCommand, commandLine, w],
  FileStream: TYPE USING [Create, GetLeaderProperties],
  Heap: TYPE USING [systemZone],
  OSMiscOps: TYPE USING [FileError, FindFile, GenerateUniqueId],
  PGSConDefs: TYPE USING [],
  Runtime: TYPE USING [GetBcdTime],
  Stream: TYPE USING [Handle, Delete],
  Strings: TYPE USING [
    String, SubStringDescriptor, AppendChar, AppendString, EquivalentSubStrings],
  TableCommand: TYPE USING [CompileStrings, MakeModule],
  Time: TYPE USING [Append, Current, Unpack],
  TTY: TYPE USING [Handle, PutChar, PutDecimal],
  UserTerminal: TYPE USING [CursorArray, GetCursorPattern, SetCursorPattern];

CrossTableControl: PROGRAM
    IMPORTS
      CharIO, Exec, FileStream, Heap, OSMiscOps, Runtime, Stream, Strings,
      TableCommand, Time, TTY, UserTerminal
    EXPORTS TableCommand, PGSConDefs = {

  CR: CHAR = '\n;

 -- cursor control

  savedCursor: UserTerminal.CursorArray;

  TableCursor: UserTerminal.CursorArray = [
    177777b, 177777b, 177777b, 160007b,
    160007b, 160007b, 160007b, 160007b, 160007b, 160007b, 160007b, 160007b,
    160007b, 177777b, 177777b, 177777b];


 -- OS interface
 
  zone: PUBLIC UNCOUNTED ZONE;


 -- command gathering and logging

  log: Stream.Handle ← NIL;
 
  tty: TTY.Handle;
  
  LogChar: PROC [c: CHAR] = {
    IF log # NIL THEN CharIO.PutChar[log, c];
    tty.PutChar[c]};
    
  LogString: PROC [s: Strings.String] = {
    IF log # NIL THEN CharIO.PutString[log, s];
    FOR i: CARDINAL IN [0..s.length) DO tty.PutChar[s[i]] ENDLOOP};
 
  LogDecimal: PROC [n: INTEGER] = {
    IF log # NIL THEN CharIO.PutDecimal[log, n];
    tty.PutDecimal[n]};
    
    
  command: Exec.CommandLine;
  
  Rubout: ERROR = CODE;
  
  CommandEnded: PROC RETURNS [BOOL] = {
    RETURN [command.i >= command.s.length]};
     
  CommandChar: PROC RETURNS [char: CHAR] = {
    IF command.i < command.s.length THEN {
      char ← command.s[command.i];  command.i ← command.i+1}
    ELSE char ← '\000;
    RETURN};
     
  ReadCommand: PROC [name, switches: Strings.String] RETURNS [BOOL] = {
    input: STRING ← [80];
    i: CARDINAL;
    c: CHAR;
    activeString: Strings.String ← name;
    DO
      IF CommandEnded[] THEN EXIT;
      c ← CommandChar[];
      SELECT c FROM
        '  => IF input.length # 0 THEN EXIT;
        CR => EXIT;
        ENDCASE => Strings.AppendChar[input, c];
      ENDLOOP;
    i ← name.length ← switches.length ← 0;
    WHILE i < input.length AND input[i] = '  DO i ← i+1  ENDLOOP;
    --parse command--
    FOR i IN [i..input.length) DO
      SELECT (c←input[i]) FROM
        '/ => activeString ← switches;
        ' , CR => EXIT;
        ENDCASE => Strings.AppendChar[activeString,c];
      ENDLOOP;
    FOR i IN [0..switches.length) DO -- convert all to lower case
      IF (c←switches[i]) IN ['A..'Z] THEN switches[i] ← VAL[(c.ORD -'A.ORD) + 'a.ORD];
      ENDLOOP;
    RETURN [name.length # 0 OR switches.length # 0]};
  
  DefaultFileName: PROC [name, defaultExtension: Strings.String] = {
    FOR i: CARDINAL IN [0..name.length) DO IF name[i] = '. THEN RETURN ENDLOOP;
    Strings.AppendString[name, defaultExtension]};

  ExtensionIs: PROC [name, ext: Strings.String] RETURNS [BOOL] = {
    dn, dx: Strings.SubStringDescriptor;
    IF name.length <= ext.length THEN RETURN [FALSE];
    dn ← [name, name.length-ext.length, ext.length];
    dx ← [ext, 0, ext.length];
    RETURN [Strings.EquivalentSubStrings[@dn, @dx]]};


  CreateTime: PUBLIC PROC [s: Stream.Handle] RETURNS [time: LONG INTEGER] = {
    RETURN [FileStream.GetLeaderProperties[s].create]};

  GenerateVersion: PUBLIC PROC RETURNS [version: BcdDefs.VersionStamp] = {
     RETURN [OSMiscOps.GenerateUniqueId[]]};


  WriteHerald: PROC = {
    t: STRING ← [20];
    LogString["Cedar Trinity Cross Table Compiler of "L];
    Time.Append[t, Time.Unpack[Runtime.GetBcdTime[]]];
    t.length ← t.length - 3;
    LogString[t]; LogChar[CR];
    t.length ← 0;
    Time.Append[t, Time.Unpack[Time.Current[]]];
    t.length ← t.length - 3;
    LogString["TableCompiler.log"L]; LogString[" -- "L];
    LogString[t];  LogChar[CR]};


-- PGSConDefs error logging interface

  warningsLogged: PUBLIC BOOL;

  seterrstream, resetoutstream: PUBLIC PROC = {NULL};
  outeol: PUBLIC PROC [n: INTEGER] = {
    THROUGH [1..n] DO LogChar['\n] ENDLOOP};
  outstring: PUBLIC PROC [string: Strings.String] = LogString; 
  
-- version identification

  sourceName: PUBLIC Strings.String ← NIL;
  pgsVersion: PUBLIC BcdDefs.VersionStamp ← [0, 0, Runtime.GetBcdTime[]];
  sourceVersion: PUBLIC BcdDefs.VersionStamp ← [0, 0, 0];
  objectVersion: PUBLIC BcdDefs.VersionStamp ← [0, 0, 0];


 Main: PROC = {

  input: {strings, binary};		-- compiling strings
  compact: BOOL;
  dStar: BOOL;
  
  rootName: STRING ← [100];
  interfaceName: STRING ← [100];
  formatName: STRING ← [100];
  bcdName: STRING ← [100];

  GetCommand: PROC = {
    file: STRING ← [100];
    sw: STRING ← [10];
    i: CARDINAL;
    c: CHAR;
    done: BOOL ← FALSE;
    sense: BOOL;

    GetSwitch: PROC RETURNS [c: CHAR] = {
      sense ← TRUE;
      WHILE i < sw.length DO
        c ← sw[i];  i ← i + 1;
        IF c = '- OR c = '~ THEN sense ← ~sense ELSE EXIT;
        ENDLOOP;
      RETURN};

    sourceName ← NIL;
    bcdName.length ← interfaceName.length ← formatName.length ← 0;
    dStar ← TRUE;
    file.length ← 0;
    IF ~ReadCommand[file, sw] OR file.length = 0 THEN RETURN;
    DefaultFileName[file, ".mesa"L];
    IF ExtensionIs[file, ".mesa"L] THEN {input ← $strings; compact ← TRUE}
    ELSE input ← $binary; 
    i ← 0;
    WHILE i < sw.length DO
      SELECT (c←GetSwitch[]) FROM
        'a => dStar ← ~sense;
        'c => compact ← sense;
        'm => input ← $binary;
        's => {input ← $strings; compact ← FALSE};
        't => {input ← $strings; compact ← TRUE};
        'g => done ← TRUE;
	ENDCASE => GO TO barf;
      REPEAT
	barf => ERROR Rubout;
      ENDLOOP;
    sourceName ← zone.NEW[StringBody[file.length]];
    Strings.AppendString[sourceName, file];
    WHILE ~done AND ReadCommand[file, sw] DO
      i ← 0;
      WHILE i < sw.length DO
        SELECT (c←GetSwitch[]) FROM
          'f => {
            DefaultFileName[file, ".format"L];
            formatName.length ← 0; Strings.AppendString[formatName, file]};
          'i => {
            DefaultFileName[file, ".bcd"L];
            interfaceName.length ← 0; Strings.AppendString[interfaceName, file]};
          'o => {
            DefaultFileName[file, ".bcd"L];
            bcdName.length ← 0; Strings.AppendString[bcdName, file]};
          'g => done ← TRUE;
          ENDCASE => ERROR Rubout;
        ENDLOOP;
      ENDLOOP};

  LogCommand: PROC = {
    SELECT input FROM
      $strings => {
        LogString["\nCompiling "L]; LogString[sourceName];
        LogString[", exporting "L]; LogString[rootName];
        LogString[" to "L]; LogString[interfaceName];
	IF bcdName.length # 0 THEN {LogString[", BCD to "L]; LogString[bcdName]};
	LogString["\nRecord format on "L]; LogString[formatName]};
      $binary => {
        LogString["\nProcessing "L]; LogString[sourceName];
        LogString[", exporting "L]; LogString[rootName];
        LogString[" to "L]; LogString[interfaceName];
	IF bcdName.length # 0 THEN {LogString[", BCD to "L]; LogString[bcdName]}};
      ENDCASE;
    LogChar[CR]};

   -- initialization

    zone ← Heap.systemZone;
    command ← Exec.commandLine;
    tty ← Exec.w;
    log ← FileStream.Create[OSMiscOps.FindFile["TableCompiler.log"L, write]];
    WriteHerald[];

   -- main loop

    DO
      BEGIN
      ENABLE
	OSMiscOps.FileError => {
	  LogString["Can't open "L]; LogString[name]; LogChar[CR];
	  GO TO FileFault};
      GetCommand[ ! Rubout => {GOTO Abort}];
      IF sourceName = NIL THEN EXIT;
      rootName.length ← 0;
      FOR i: CARDINAL IN [0..sourceName.length) DO
	IF sourceName[i] = '. THEN EXIT;
	Strings.AppendChar[rootName, sourceName[i]];
	ENDLOOP;
      IF interfaceName.length = 0 THEN Strings.AppendString[interfaceName, "SELF"L];
      IF formatName.length = 0 THEN {
	Strings.AppendString[formatName, rootName];
	Strings.AppendString[formatName, "Format."L]};
      LogCommand[];
      IF bcdName.length = 0 THEN {
	Strings.AppendString[bcdName, rootName];
	Strings.AppendString[bcdName, ".bcd"L]};

      savedCursor ← UserTerminal.GetCursorPattern[];
      UserTerminal.SetCursorPattern[TableCursor];
      SELECT input FROM
	$strings => {
          nChars, nStrings: CARDINAL;
	  [nStrings, nChars] ← TableCommand.CompileStrings[
	    inputFile: sourceName,
	    interfaceId: interfaceName,
	    formatId: formatName,
	    moduleId: bcdName,
	    compact: compact,
	    altoCode: ~dStar];
	  LogString["Strings: "L];  LogDecimal[nStrings];
	  LogString[", characters: "L];  LogDecimal[nChars];
	  LogChar[CR]};
	$binary =>
          TableCommand.MakeModule[
	    inputFile: sourceName,
	    interfaceId: interfaceName,
	    moduleId: bcdName,
	    altocode: ~dStar];
	ENDCASE;
      LogChar[CR];
      UserTerminal.SetCursorPattern[savedCursor];
      EXITS
	FileFault => NULL;
	Abort => LogChar['?];
      END;
      IF sourceName # NIL THEN zone.FREE[@sourceName];
      ENDLOOP;

    IF log # NIL THEN Stream.Delete[log];
    zone ← NIL};

  Exec.AddCommand["CrossTableCompiler.~"L, Main];
  
  }.