-- BLControl.mesa
-- Last edited by Satterthwaite on January 6, 1983 2:19 pm	

DIRECTORY
  CharIO: TYPE USING [PutChar, PutLine, PutString],
  CommandUtil: TYPE USING [
    CommandObject, CopyString, Echo, Failed, FreePairList, FreeString,
    GetNth, ListLength, PairList, Parse, SetExtension],
  Exec: TYPE USING [AddCommand, commandLine, w],
  ExecOps: TYPE USING [CheckForAbort, Command],
  ListerOps: TYPE USING [ListFiles, ListBcd, ListRTBcd, ListStamps, ListVersion],
  OSMiscOps: TYPE USING [BcdCreateTime],
  Runtime: TYPE USING [CallDebugger], 
  Stream: TYPE USING [
    Handle, Object, PutByteProcedure, DeleteProcedure, Delete],
  Strings: TYPE USING [
    AppendString, EquivalentSubString, String, SubStringDescriptor],
  Time: TYPE USING [Append, AppendCurrent, Current, Unpack],
  TTY: TYPE USING [PutChar];

BLControl: PROGRAM
    IMPORTS
      CharIO, CommandUtil, Exec, ExecOps, ListerOps, OSMiscOps,
      Runtime, Stream, Strings, Time, TTY = {

  EquivalentString: PROC [s1, s2: Strings.String] RETURNS [BOOL] = {
    ss1: Strings.SubStringDescriptor ← [base: s1, offset: 0, length: s1.length];
    ss2: Strings.SubStringDescriptor ← [base: s2, offset: 0, length: s2.length];
    RETURN [Strings.EquivalentSubString[@ss1, @ss2]]};


 -- command gathering and logging

  log: Stream.Handle ← NIL;
  logStreamObject: Stream.Object;

  SetLogStream: PROC = {
    logStreamObject ← [
      options: TRASH, 
      getByte: TRASH, putByte: PutTTY, 
      getWord: TRASH, putWord: TRASH,
      get: TRASH, put: TRASH, 
      setSST: TRASH, sendAttention: TRASH, waitAttention: TRASH, 
      delete: NullDelete];
    log ← @logStreamObject};

  PutTTY: Stream.PutByteProcedure = {(Exec.w).PutChar[VAL[byte]]};
  NullDelete: Stream.DeleteProcedure = {};
  
  LogString: PROC [s: Strings.String] = {CharIO.PutString[log, s]};
  LogChar: PROC [c: CHAR] = {CharIO.PutChar[log, c]};

  renamedOutput: BOOL ← FALSE;

  RepeatCommand: PROC [s: Stream.Handle] = {
    OPEN CharIO;
    IF outputName # NIL THEN {
      PutString[s, "\nListing "L]; PutString[s, inputName];
      PutString[s, ", output to "L]; PutString[s, outputName]};
    PutChar[s, '\n]};


  WriteHerald: PROC [s: Stream.Handle, id: Strings.String] = {
    OPEN Time, CharIO;
    herald: STRING ← [60];
    Strings.AppendString[herald, "Cedar 3.4 Bcd Lister of "L];
    Time.Append[herald, Time.Unpack[[OSMiscOps.BcdCreateTime[]]]];
    herald.length ← herald.length-3;
    PutLine[s, herald];
    IF id # NIL THEN {PutString[s, id]; PutString[s, " -- "L]};
    herald.length ← 0; Time.AppendCurrent[herald]; PutLine[s, herald]};


 -- file name bookkeeping

  inputName, outputName, rootName: Strings.String ← NIL;
  commandArgs: CommandUtil.PairList;

  FileRoot: PROC [s: Strings.String] RETURNS [root: Strings.String] = {
    root ← CommandUtil.CopyString[s, 0];
    FOR i: CARDINAL DECREASING IN [0..root.length) DO 
      IF root[i] = '. THEN {root.length ← i; EXIT};  
      ENDLOOP};


 -- switches
 
  StandardDefaults: PACKED ARRAY CHAR ['a..'z] OF BOOL = [
      FALSE, -- a
      TRUE,  -- b  Bcd (only)
      FALSE, -- c
      FALSE, -- d  Call debugger on error
      FALSE, -- e
      FALSE, -- f  Files (only)
      FALSE, -- g
      FALSE, -- h
      FALSE, -- i
      FALSE, -- j
      FALSE, -- k
      FALSE, -- l  Links
      FALSE, -- m
      FALSE, -- n
      FALSE, -- o  Ordered (with /r only)
      FALSE, -- p
      FALSE, -- q
      FALSE, -- r  RTBcd (only)
      FALSE, -- s  Stamps (only)
      FALSE, -- t
      FALSE, -- u
      FALSE, -- v  Version (only)
      FALSE, -- w
      FALSE, -- x
      FALSE, -- y
      FALSE];-- z
    -- v > b > r > f > s; others are unused
  

-- Exec interfaces

DoCommand: PROC = { 
  theCommand: CommandUtil.CommandObject ← [
    pos: Exec.commandLine.i,
    len: Exec.commandLine.s.length,
--  data: @Exec.commandLine.s.text];
    data: LOOPHOLE[Exec.commandLine.s + StringBody[0].SIZE]];
  results: CommandUtil.PairList;
  switches: Strings.String;
  defaultSwitches: PACKED ARRAY CHAR ['a..'z] OF BOOL ← StandardDefaults;
  localSwitches: PACKED ARRAY CHAR ['a..'z] OF BOOL;

  SetLogStream[];
  WriteHerald[log, NIL];
  
 -- main loop

  DO {

    Initialize: PROC = INLINE {RepeatCommand[log]};

    Finalize: PROC = INLINE {};

    startTime: LONG CARDINAL;

    localSwitches ← defaultSwitches;
    [inputName, commandArgs, results, switches] ←
      CommandUtil.Parse[
		s: @theCommand,
		opX: 2+("bcd"L).length, resultX: 2+("bl"L).length
	  ! CommandUtil.Failed => {GO TO badSyntax}];
    IF inputName = NIL AND switches = NIL THEN EXIT;  -- done listing
    LogString["\nCommand: "L];
    CommandUtil.Echo[
      d: log, operator: inputName, argList: commandArgs, 
      resultList: results, switches: switches];
    IF inputName = NIL THEN GO TO globalSwitches;
    SELECT CommandUtil.ListLength[results] FROM
      0 => NULL;
      1 => outputName ← CommandUtil.GetNth[list: results, n: 0];
      ENDCASE => GO TO badSemantics;
    rootName ← FileRoot[inputName];
    IF switches # NIL THEN {
      i: CARDINAL ← 0;
      sense: BOOL ← TRUE;
      WHILE i < switches.length DO
	c: CHAR = switches[i];
	SELECT c FROM
	  '-, '~ => sense ← ~sense;
	  IN ['a..'z] => {localSwitches[c] ← sense; sense ← TRUE};
	  IN ['A..'Z] => {localSwitches[VAL[(c.ORD-'A.ORD) + 'a.ORD]] ← sense; sense ← TRUE};
	  '! => {Runtime.CallDebugger[NIL]; sense ← TRUE};
	  ENDCASE;
	i ← i+1;
	ENDLOOP;
      switches ← CommandUtil.FreeString[switches]};

    startTime ← Time.Current[];
    
    SELECT TRUE FROM
      localSwitches['v] => NULL;	-- for now
      ENDCASE => {
        IF outputName = NIL THEN
	  outputName ← CommandUtil.CopyString[rootName, 2+("bl"L).length];
        outputName ← CommandUtil.SetExtension[outputName, "bl"L]};
    Initialize[];
      BEGIN
      ENABLE UNWIND => {Finalize[]};
      SELECT TRUE FROM
	localSwitches['v] => ListerOps.ListVersion[rootName];
	localSwitches['s] => ListerOps.ListStamps[rootName, outputName];
	localSwitches['f] => ListerOps.ListFiles[rootName, outputName];
	localSwitches['r] => ListerOps.ListRTBcd[rootName, outputName,  localSwitches['o]];
	localSwitches['b] => ListerOps.ListBcd[rootName, outputName, localSwitches['l]];
	ENDCASE;
      END;
    Finalize[];
    IF ExecOps.CheckForAbort[] THEN EXIT;
    
    EXITS
      globalSwitches => {
	sense: BOOL ← TRUE;
        results ← CommandUtil.FreePairList[results];
        FOR i: CARDINAL IN [0..switches.length) DO
	  c: CHAR = switches[i];
	  SELECT c FROM
	    '-, '~ => sense ← ~sense;
	    '! => Runtime.CallDebugger[NIL];
	    IN ['a..'z] => {defaultSwitches[c] ← sense; sense ← TRUE};
	    IN ['A..'Z] => {defaultSwitches[VAL[(c.ORD-'A.ORD) + 'a.ORD]] ← sense; sense ← TRUE};
	    ENDCASE => EXIT;
	  ENDLOOP;
        switches ← CommandUtil.FreeString[switches]};
      badSemantics => {
        results ← CommandUtil.FreePairList[results];
        LogString["\n -- Illegal command"L]}};
    inputName ← CommandUtil.FreeString[inputName];
    outputName ← CommandUtil.FreeString[outputName];
    commandArgs ← CommandUtil.FreePairList[commandArgs];
    rootName ← CommandUtil.FreeString[rootName];
    REPEAT
      badSyntax => {LogString["-- Illegal syntax"L]};
    ENDLOOP;

  Stream.Delete[log];  log ← NIL};
  
  
-- global Binder initialization
  
  Init: PROC = {
    Exec.AddCommand["bl"L, DoCommand];
    Exec.AddCommand["BcdLister"L, DoCommand]};
  
  Init[];

  }.