DIRECTORY
  Ascii USING [NUL],
  CLibraryDefs USING [cNULL, Error, exit, open],
  UnixDefs USING [numOpenFiles, fileInfo, openFiles],
  MFile USING [Error, Handle, ReadOnly],
  MStream USING [Handle],
  MLoader USING [Handle, Run],
  Heap USING [systemZone],
  Put USING [Line, LongString],
  Tool USING [Create, MakeTTYSW, MakeSWsProc, UnusedLogName],
  ToolWindow USING [TransitionProcType],
  TTY USING [EchoClass],
  TTYSW USING [SetEcho, GetLine],
  Window USING [Handle];

UnixImpl: PROGRAM
  IMPORTS MFile, MLoader, CLibraryDefs, Heap, Put, Tool, TTYSW, UnixDefs
  EXPORTS UnixDefs = {
  
  DataHandle: TYPE = LONG POINTER TO Data;
  Data: TYPE = MACHINE DEPENDENT RECORD [
    ttySW(0): Window.Handle ← NIL
    ];

  -- Variable declarations

  ttyWindow: PUBLIC Window.Handle;
  openFiles: PUBLIC ARRAY [0..UnixDefs.numOpenFiles) OF UnixDefs.fileInfo;
  argcT: LONG INTEGER ← 0;
  argvT: LONG POINTER TO LONG POINTER TO INTEGER ← NIL;
  argvRef: ARRAY [0..10) OF LONG POINTER TO INTEGER ← ALL[NIL];
  argvRefRef: ARRAY [0..10) OF ARRAY [0..30) OF INTEGER ← ALL[ALL[0]];
  p: PROCESS RETURNS [];
  toolData: DataHandle ← NIL;
  wh: Window.Handle;  -- Tool's window

  WatchTTY: PROCEDURE = {
    -- Do the tasks necessary to execute a form subwindow command.
    -- Again, if the command will take a long time, then one might FORK a PROCESS to do it.
    
    oldEcho: TTY.EchoClass;
    userInput: LONG STRING ← [100];
    fileName: LONG STRING ← [50];
    iLine, iArg, iChar, commandLength: CARDINAL;
    redirect: INTEGER;
    
    commandFile: MFile.Handle;
    commandHandle: MLoader.Handle;
    
    Put.Line[toolData.ttySW, "Simple Shell Starting."L];
    oldEcho ← TTYSW.SetEcho[ttyWindow, plain];
  DO -- forever
    -- reset the standard input, output and error
    UnixDefs.openFiles[0].windowHandle ← toolData.ttySW;
    UnixDefs.openFiles[0].type ← window;
    UnixDefs.openFiles[1].windowHandle ← toolData.ttySW;
    UnixDefs.openFiles[1].type ← window;
    UnixDefs.openFiles[2].windowHandle ← toolData.ttySW;
    UnixDefs.openFiles[2].type ← window;
    
    -- put out the shell prompt and get a command line of input
    Put.LongString[toolData.ttySW, "% "L];
    TTYSW.GetLine[ttyWindow, userInput];
    
    -- parse the command line arguments
    userInput.text[userInput.length] ← ' ; -- blank at end
    iArg ← 0;
    iLine ← 0;
    argcT ← 0;
    WHILE iLine < userInput.length DO
        WHILE userInput.text[iLine] = '  DO -- skip blanks
            iLine ← iLine + 1;  ENDLOOP;
	iChar ← 0;
	redirect ← -1;
	IF userInput.text[iLine] = '< THEN {
	    redirect ← 0;
	    iLine ← iLine + 1; }
	ELSE IF userInput.text[iLine] = '> THEN {
	    redirect ← 1;
	    iLine ← iLine + 1; };
	WHILE userInput.text[iLine] # '  DO -- copy word
            argvRefRef[iArg][iChar] ← userInput.text[iLine]-0C;
	    iLine ← iLine + 1;
	    iChar ← iChar + 1;
	    ENDLOOP;
	argvRefRef[iArg][iChar] ← Ascii.NUL-0C;
	IF iArg = 0 THEN commandLength ← iLine;
	SELECT redirect FROM
	    -1 => iArg ← iArg + 1;
	    0, 1 => {
	        UnixDefs.openFiles[redirect].type ← unused;
		IF CLibraryDefs.open[argvRef[iArg], 2] # redirect THEN
		    CLibraryDefs.Error[
		        "Can't open redirected input or output file.\n"L];
	        };
	    ENDCASE => ERROR;
	ENDLOOP;
    argcT ← iArg;
    userInput.text[commandLength] ← '.;
    userInput.text[commandLength+1] ← 'b;
    userInput.text[commandLength+2] ← 'c;
    userInput.text[commandLength+3] ← 'd;
    userInput.length ← commandLength+4;
    {
        commandFile ← MFile.ReadOnly[userInput, [] !
            MFile.Error => {
                GO TO BadCommand; }; ];
        commandHandle ← MLoader.Run[commandFile];
	EXITS
            BadCommand => Put.Line[toolData.ttySW, "Command not found."L];  };
    userInput.length ← 0;
    CLibraryDefs.exit[0];
    ENDLOOP;
    -- later we should put this back in
    -- MLoader.Unload[commandHandle];
    };
       
  ClientTransition: ToolWindow.TransitionProcType = {
    SELECT TRUE FROM
      old = inactive => {
        IF toolData = NIL THEN toolData ← Heap.systemZone.NEW[Data ← []];
        argvT ← @argvRef[0];
        FOR i: CARDINAL IN [0..10) DO
          argvRef[i] ← @argvRefRef[i][0];
          ENDLOOP;
	};
      new = inactive =>
        IF toolData # NIL THEN {
	  Heap.systemZone.FREE[@toolData]};
      ENDCASE;
    };

  Init: PROCEDURE = {
    wh ← Tool.Create[
      makeSWsProc: MakeSWs, initialState: default,
      initialBox: [[0, 70], [500, 600]],
      clientTransition: ClientTransition,
      name: "Unix"L, tinyName1: "Unix"L];
     };

  MakeSWs: Tool.MakeSWsProc = {
    logName: STRING ← [40];
    Tool.UnusedLogName[unused: logName, root: "Unix.log"L];
    toolData.ttySW ← Tool.MakeTTYSW[window: window, name: logName];
    ttyWindow ← toolData.ttySW;
    };
    
  GetArgs: PUBLIC PROCEDURE [argc: LONG POINTER TO LONG INTEGER,
    argv: LONG POINTER TO LONG POINTER TO LONG POINTER TO INTEGER] = {
    argv↑ ← argvT;
    argc↑ ← argcT;
    };

  -- Mainline code

  Init[];
  FOR i: CARDINAL IN [0..UnixDefs.numOpenFiles) DO
      UnixDefs.openFiles[i].type ← unused;
      UnixDefs.openFiles[i].windowHandle ← NIL;
      UnixDefs.openFiles[i].streamHandle ← NIL;
      ENDLOOP;
  p ← FORK WatchTTY[];

  }...