-- TableControl.Mesa  Edited by Satterthwaite on August 29, 1980  1:33 PM

DIRECTORY
  AltoDisplay: TYPE USING [DCB, DCBHandle, DCBchainHead],
  AltoFileDefs: TYPE USING [CFA],
  BcdDefs: TYPE USING [BCD, VersionStamp],
  CharIO: TYPE USING [CR, SP, PutChar, PutDecimal, PutString],
  FrameOps: TYPE USING [CodeHandle, MyGlobalFrame],
  Inline: TYPE USING [LongDiv, LongMult],
  ImageDefs: TYPE USING [FileRequest, RunImage, StopMesa],
  MiscDefs: TYPE USING [CommandLineCFA],
  OsStaticDefs: TYPE USING [OsStatics],
  SegmentDefs: TYPE USING [
    Append, DefaultVersion, DeleteFileSegment, FileError, FileHandle, FileNameError,
    FileSegmentHandle, GetFileTimes, InsertFile, NewFile, NewFileSegment,
    Read, SegmentAddress, SwapIn, Unlock, Write],
  StreamDefs: TYPE USING [
    Append, CreateByteStream, JumpToFA,
    Read, StreamError, StreamHandle, Write],
  StringDefs: TYPE USING [
    AppendChar, AppendString, EquivalentString, MesaToBcplString,
    WordsForBcplString],
  TableCommand: TYPE USING [CompileStrings, MakeModule],
  TimeDefs: TYPE USING [AppendDayTime, DefaultTime, UnpackDT];

TableControl: PROGRAM
    IMPORTS
      CharIO, FrameOps, ImageDefs, Inline, MiscDefs, SegmentDefs,
      StreamDefs, StringDefs, TableCommand, TimeDefs
    EXPORTS TableCommand =
  BEGIN OPEN StreamDefs;

  CR: CHARACTER = CharIO.CR;

 -- cursor control

  CursorBits: TYPE = ARRAY [0..16) OF WORD;

  TheCursor: POINTER TO CursorBits = LOOPHOLE[431B];
  savedCursor: CursorBits;

  TableCursor: CursorBits = [
    177777b, 177777b, 177777b, 160007b,
    160007b, 160007b, 160007b, 160007b, 160007b, 160007b, 160007b, 160007b,
    160007b, 177777b, 177777b, 177777b];


 -- command gathering and logging

  log: StreamHandle;
  logName: STRING = "TableCompiler.Log.";
  logRequest: ImageDefs.FileRequest ← [
    name: logName, file: NIL, access: Write+Append, link: ];

  commandStream: StreamHandle;
  comCmRequest: ImageDefs.FileRequest ← [
    name: "Com.Cm.", file: NIL, access: Read, link: ];

  Rubout: ERROR = CODE;

  ReadCommand: PROC [name, switches: STRING] RETURNS [BOOLEAN] = {
    input: STRING ← [80];
    i: CARDINAL;
    activeString: STRING ← name;
    c: CHARACTER;
      DO
      c ← commandStream.get[commandStream ! StreamDefs.StreamError => EXIT];
      SELECT c FROM
        CharIO.SP => IF input.length # 0 THEN EXIT;
        CR => EXIT;
        ENDCASE => StringDefs.AppendChar[input, c];
      ENDLOOP;
    i ← name.length ← switches.length ← 0;
    WHILE i < input.length AND input[i] = CharIO.SP DO i ← i+1  ENDLOOP;
    --parse command--
    FOR i IN [i..input.length)
      DO
      SELECT (c←input[i]) FROM
        '/ => activeString ← switches;
        CharIO.SP, CR => EXIT;
        ENDCASE => StringDefs.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] ← c + ('a-'A);
      ENDLOOP;
    RETURN [name.length # 0 OR switches.length # 0]};
  
  DefaultFileName: PROC [name, defaultExtension: STRING] = {
    FOR i: CARDINAL IN [0..name.length) DO IF name[i] = '. THEN RETURN ENDLOOP;
    StringDefs.AppendString[name, defaultExtension]};

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

  GetCommand: PROC = {
    file: STRING ← [40];
    sw: STRING ← [10];
    i: CARDINAL;
    c: CHARACTER;
    done: BOOLEAN ← FALSE;
    sense: BOOLEAN;

    GetSwitch: PROC RETURNS [c: CHARACTER] = {
      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.length ← bcdName.length ← interfaceName.length ← formatName.length ← 0;
    dStar ← FALSE;
    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;
        'r => {
	  FOR j: CARDINAL IN [i..sw.length) DO sw[j-i] ← sw[j] ENDLOOP;
	  sw.length ← sw.length - i;
	  DefaultFileName[file, ".image"L];
	  Run[file, sw ! UNWIND => NULL; ANY => GO TO barf]};
	ENDCASE => GO TO barf;
      REPEAT
	barf => ERROR Rubout;
      ENDLOOP;
    StringDefs.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; StringDefs.AppendString[formatName, file]};
          'i => {
            DefaultFileName[file, ".bcd"L];
            interfaceName.length ← 0; StringDefs.AppendString[interfaceName, file]};
          'o => {
            DefaultFileName[file, ".bcd"L];
            bcdName.length ← 0; StringDefs.AppendString[bcdName, file]};
          'g => done ← TRUE;
          ENDCASE => ERROR Rubout;
        ENDLOOP;
      ENDLOOP};

  Run: PROC [name, otherSwitches: STRING] = {
    OPEN SegmentDefs;
    c: CHARACTER;
    i: CARDINAL;
    copy: StreamDefs.StreamHandle;
    copy ← StreamDefs.CreateByteStream[comCmRequest.file, Write+Append];
    FOR i IN [0..name.length) DO copy.put[copy, name[i]] ENDLOOP;
    IF otherSwitches.length # 0
      THEN {
	copy.put[copy, '/];
	FOR i IN [0..otherSwitches.length) DO copy.put[copy, otherSwitches[i]] ENDLOOP};
    copy.put[copy, ' ];
      DO
      c ← commandStream.get[commandStream ! StreamDefs.StreamError => GO TO done];
      copy.put[copy, c];
      REPEAT
	done => commandStream.destroy[commandStream];
      ENDLOOP;
    copy.destroy[copy];
    IF ExtensionIs[name, ".run"L]
      THEN {
	p: POINTER = OsStaticDefs.OsStatics.EventVector;
	EVItem: TYPE = MACHINE DEPENDENT RECORD [
	    type: [0..7777B], length: [0..17B]];
	p↑ ← EVItem[6, StringDefs.WordsForBcplString[name.length]+1];
	StringDefs.MesaToBcplString[name, p+1];
	ImageDefs.StopMesa[]}
      ELSE ImageDefs.RunImage[
	NewFileSegment[NewFile[name, Read, DefaultVersion], 1, 1, Read]]};

  ExtensionIs: PROC [name, ext: STRING] RETURNS [BOOLEAN] = {
    t: STRING ← [40];
    IF name.length <= ext.length THEN RETURN[FALSE];
    FOR i: CARDINAL IN [name.length-ext.length .. name.length)
      DO  StringDefs.AppendChar[t,name[i]]  ENDLOOP;
    RETURN [StringDefs.EquivalentString[t,ext]]};

  RepeatCommand: PROC [s: StreamHandle] = {
    OPEN CharIO;
    PutChar[s, CR];
    SELECT input FROM
      strings => {
        PutString[s, "Compiling "L]; PutString[s, sourceName];
        PutString[s, ", exporting "L]; PutString[s, rootName];
        PutString[s, " to "L]; PutString[s, interfaceName];
	IF bcdName.length # 0
	  THEN {PutString[s, ", BCD to "L]; PutString[s, bcdName]};
	PutChar[s, CR];
	PutString[s, "Record format on "L]; PutString[s, formatName]};
      binary => {
        PutString[s, "Processing "L]; PutString[s, sourceName];
        PutString[s, ", exporting "L]; PutString[s, rootName];
        PutString[s, " to "L]; PutString[s, interfaceName];
	IF bcdName.length # 0
	  THEN {PutString[s, ", BCD to "L]; PutString[s, bcdName]}};
      ENDCASE;
    PutChar[s, CR]};


  CreateTime: PUBLIC PROC [s: StreamDefs.StreamHandle]
      RETURNS [time: LONG INTEGER] = {
    RETURN [WITH s: s SELECT FROM
      Disk => SegmentDefs.GetFileTimes[s.file].create,
      ENDCASE => 0]};

  MyBcdVersion: PUBLIC PROC RETURNS [version: BcdDefs.VersionStamp] = {
    OPEN SegmentDefs;
    cseg: FileSegmentHandle = FrameOps.CodeHandle[FrameOps.MyGlobalFrame[]];
    bcdseg: FileSegmentHandle = NewFileSegment[cseg.file, 1, 1, Read];
    bcd: POINTER TO BcdDefs.BCD;
    SwapIn[bcdseg];
    bcd ← SegmentAddress[bcdseg];
    version ← bcd.version;
    Unlock[bcdseg];  DeleteFileSegment[bcdseg];
    RETURN};


  WriteHerald: PROC [stream: StreamHandle, name: STRING] = {
    OPEN TimeDefs, CharIO;
    version: BcdDefs.VersionStamp = MyBcdVersion[];
    t: STRING ← [20];
    PutString[stream, "Alto/Mesa TableCompiler 6.0C of "L];
    AppendDayTime[t, UnpackDT[version.time]];
    t.length ← t.length - 3;
    PutString[stream, t]; PutChar[stream, CR];
    t.length ← 0;
    AppendDayTime[t,UnpackDT[DefaultTime]];
    t.length ← t.length - 3;
    IF name # NIL THEN {PutString[stream, name]; PutString[stream, " -- "L]};
    PutString[stream, t];  PutChar[stream, CR]};

  -- timing procedures

  timer: POINTER TO CARDINAL = LOOPHOLE[430B];

  TimeNow: PROC RETURNS [CARDINAL] = INLINE {RETURN [timer↑]};

  TimeSince: PROC [start: CARDINAL] RETURNS [CARDINAL] = {
    OPEN Inline; RETURN [(LongDiv[LongMult[timer↑-start, 2*39], 1000]+1)/2]};


 -- initialization

  dcbSpace: ARRAY [0..SIZE[AltoDisplay.DCB]+1) OF UNSPECIFIED;
  dcb, saveDCB: AltoDisplay.DCBHandle;

  dcb ← @dcbSpace[0];
  IF LOOPHOLE[dcb, CARDINAL] MOD 2 # 0 THEN dcb ← dcb + 1;
  dcb↑ ← AltoDisplay.DCB[NIL, high, black, 0, 0, NIL, 0];
  saveDCB ← AltoDisplay.DCBchainHead↑;  AltoDisplay.DCBchainHead↑ ← dcb;

  -- find the command stream
    BEGIN OPEN StreamDefs;
    cfa: POINTER TO AltoFileDefs.CFA = MiscDefs.CommandLineCFA[];
    comCmRequest.file ← SegmentDefs.InsertFile[@cfa.fp, Read];
    commandStream ← CreateByteStream[comCmRequest.file, Read];
    StreamDefs.JumpToFA[commandStream, @cfa.fa];
    END;
  -- find the log stream
    BEGIN OPEN SegmentDefs;
    s: STRING ← [40];
    IF logRequest.file = NIL
      THEN logRequest.file ← NewFile[logName, Write+Append, DefaultVersion];
    log ← CreateByteStream[logRequest.file, Write+Append];
    END;

  WriteHerald[log, NIL];


 -- main loop

  DO
    BEGIN
    ENABLE {
      SegmentDefs.FileNameError => {
        OPEN CharIO; PutString[log, "Can't open "]; PutString[log, name]; PutChar[log, CR];
	GO TO FileFault};
      SegmentDefs.FileError => {
	OPEN CharIO; PutString[log, "File problem"]; PutChar[log, CR];  GO TO FileFault}};
    startTime: CARDINAL;
    GetCommand[ ! Rubout => GOTO Abort];
    IF sourceName.length = 0 THEN EXIT;
    startTime ← TimeNow[];
    rootName.length ← 0;
    FOR i: CARDINAL IN [0..sourceName.length)
      DO
      IF sourceName[i] = '. THEN EXIT;
      StringDefs.AppendChar[rootName, sourceName[i]];
      ENDLOOP;
    IF interfaceName.length = 0
      THEN StringDefs.AppendString[interfaceName, "SELF"];
    IF formatName.length = 0
      THEN {
	StringDefs.AppendString[formatName, rootName];
	StringDefs.AppendString[formatName, "Format."]};
    RepeatCommand[log];
    IF bcdName.length = 0
      THEN {
	StringDefs.AppendString[bcdName, rootName];
	StringDefs.AppendString[bcdName, ".bcd"]};

    savedCursor ← TheCursor↑;  TheCursor↑ ← TableCursor;
    SELECT input FROM
      strings => {
	OPEN CharIO;
        nChars, nStrings: CARDINAL;
	[nStrings, nChars] ← TableCommand.CompileStrings[
	  inputFile: sourceName,
	  interfaceId: interfaceName,
	  formatId: formatName,
	  moduleId: bcdName,
	  compact: compact,
	  altoCode: ~dStar];
	PutString[log, "Strings: "];  CharIO.PutDecimal[log, nStrings];
	PutString[log, ", characters: "];  CharIO.PutDecimal[log, nChars];
	PutChar[log, CR]};
      binary =>
        TableCommand.MakeModule[
	  inputFile: sourceName,
	  interfaceId: interfaceName,
	  moduleId: bcdName,
	  altocode: ~dStar];
      ENDCASE;
    CharIO.PutChar[log, CR];
    TheCursor↑ ← savedCursor;
    EXITS
      FileFault => NULL;
      Abort => CharIO.PutChar[log, '?];
    END;
    ENDLOOP;

  IF log # NIL THEN log.destroy[log];
  AltoDisplay.DCBchainHead↑ ← saveDCB;
  ImageDefs.StopMesa[];

  END.