DIRECTORY
    Ascii USING [CR, NUL],
    CLibraryDefs USING [CChar, CInt, CString, FILE, cNULL, stderr],
    UnixDefs USING [GetArgs, openFiles, numOpenFiles],
    TTYSW USING [AppendChar, GetChar],
    Inline USING [LowHalf],
    String USING [AppendLongDecimal, AppendLongNumber],
    Stream USING [GetChar, PutChar, Delete, EndOfStream],
    Storage USING[AppendChar, AppendString, CopyString],
    MStream USING [Handle, Error, ReadWrite];
    
CLibraryImpl: PROGRAM IMPORTS UnixDefs, Inline, String, Storage, Stream, MStream, TTYSW
	EXPORTS CLibraryDefs = {

  -- System Calls
  
  chdir: PUBLIC PROC [directoryName: CLibraryDefs.CString]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["chdir not implemented."L];
      RETURN [0];  };
      
  chmod: PUBLIC PROC [fileName: CLibraryDefs.CString, fileMode: CLibraryDefs.CInt]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["chmod not implemented."L];
      RETURN [0];  };
      
  close: PUBLIC PROC [fileDescriptor: CLibraryDefs.FILE]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
    file: INTEGER ← Inline.LowHalf[fileDescriptor];
  
    IF file >= UnixDefs.numOpenFiles THEN {
        Error["File number too high in fclose."L];
	RETURN;  };
    IF UnixDefs.openFiles[file].type = stream THEN {
        Stream.Delete[UnixDefs.openFiles[file].streamHandle];
	UnixDefs.openFiles[file].type ← unused;  };
    };

  creat: PUBLIC PROC [fileName: CLibraryDefs.CString, fileMode: CLibraryDefs.CInt]
      RETURNS [fileDescriptor: CLibraryDefs.FILE] = {
      
      Error["creat not implemented."L];
      RETURN [CLibraryDefs.cNULL];  };

  dup: PUBLIC PROC [oldFileDescriptor: CLibraryDefs.FILE]
      RETURNS [newFileDescriptor: CLibraryDefs.FILE] = {
      
      Error["dup not implemented."L];
      RETURN [CLibraryDefs.cNULL];  };

  dup2: PUBLIC PROC [fileDescriptor1, fileDescriptor2: CLibraryDefs.FILE]
      RETURNS [fileDescriptor: CLibraryDefs.FILE] = {
      
      Error["dup2 not implemented."L];
      RETURN [CLibraryDefs.cNULL];  };

  execv: PUBLIC PROC [codeFileName: CLibraryDefs.CString, argv: LONG POINTER TO CLibraryDefs.CString]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["execv not implemented."L];
      RETURN [0];  };

  exit: PUBLIC PROC [returnCode: CLibraryDefs.CInt] = {
  
     FOR i: CARDINAL IN [0..UnixDefs.numOpenFiles) DO
         fclose[LOOPHOLE[LONG[i], CLibraryDefs.FILE]];  ENDLOOP;
     };
     
  
  fork: PUBLIC PROC []
      RETURNS [processDescriptor: CLibraryDefs.CInt] = {
      
      Error["fork not implemented."L];
      RETURN [0];  };

  fstat: PUBLIC PROC [fileDescriptor: CLibraryDefs.FILE, buffer: LONG POINTER]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["fstat not implemented."L];
      RETURN [0];  };

  link: PUBLIC PROC [fileName1, fileName2: CLibraryDefs.CString]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["link not implemented."L];
      RETURN [0];  };

  lseek: PUBLIC PROC [fileDescriptor: CLibraryDefs.FILE, offset, whence: CLibraryDefs.CInt]
      RETURNS [filePosition: CLibraryDefs.CInt] = {
      
      Error["lseek not implemented."L];
      RETURN [0];  };

  open: PUBLIC PROC [fileName: CLibraryDefs.CString, fileMode: CLibraryDefs.CInt]
      RETURNS [fileDescriptor: CLibraryDefs.FILE] = {
      
    fileString: LONG STRING ← [50];
    i: CARDINAL;
    file: INTEGER;
    
    fileDescriptor ← CLibraryDefs.cNULL;  -- return value on failure
    -- first find an empty file slot
    FOR i: CARDINAL IN [0..UnixDefs.numOpenFiles) DO
        IF UnixDefs.openFiles[i].type = unused THEN GOTO FoundIt;
    REPEAT
        FoundIt => file ← i;
	FINISHED => RETURN;
    ENDLOOP;
    -- now convert the filename string from UNIX format
    -- (ARRAY OF INTEGER) to a Mesa string
    i ← 0;
    WHILE (fileName + i)↑ # 0 DO
        fileString.text[i] ← (fileName + i)↑ + 0C;
	i ← i + 1;
	ENDLOOP;
    fileString.length ← i;
    -- Now try to open the file, catching the Error signal.
    -- Later we should look at the access type (read, write, or
    -- update) and use the appropriate ReadOnly, WriteOnly, or
    -- ReadWrite.
    {
        UnixDefs.openFiles[file].streamHandle ←
	    MStream.ReadWrite[fileString, [] !
	        MStream.Error => {
		    GOTO BadFile;  };  ];
        EXITS
	    BadFile => RETURN;
    };
    UnixDefs.openFiles[file].type ← stream;
    fileDescriptor ← LONG[file];
    };

  pipe: PUBLIC PROC [fileDescriptorArray: LONG POINTER TO CLibraryDefs.FILE]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["pipe not implemented."L];
      RETURN [0];  };

  read: PUBLIC PROC [fileDescriptor: CLibraryDefs.FILE, buffer: CLibraryDefs.CString, nBytes: CLibraryDefs.CInt]
      RETURNS [nBytesRead: CLibraryDefs.CInt] = {
      
      Error["read not implemented."L];
      RETURN [0];  };

  signal: PUBLIC PROC [signalNumber: CLibraryDefs.CInt, functionToCall: LONG POINTER]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["signal not implemented."L];
      RETURN [0];  };

  stat:	PUBLIC PROC [fileName: CLibraryDefs.CString, buffer: LONG POINTER]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["stat not implemented."L];
      RETURN [0];  };

  time:	PUBLIC PROC [timeBuffer: LONG POINTER TO CLibraryDefs.CInt]
      RETURNS [timeValue: CLibraryDefs.CInt] = {
      
      Error["time not implemented."L];
      RETURN [0];  };

  umask: PUBLIC PROC [complementedMode: CLibraryDefs.CInt] = {
      
      Error["umask not implemented."L]  };

  
  unlink: PUBLIC PROC [fileName: CLibraryDefs.CString]
      RETURNS [returnCode: CLibraryDefs.CInt] = {
      
      Error["unlink not implemented."L];
      RETURN [0];  };

  wait:	PUBLIC PROC [returnCode: LONG POINTER TO CLibraryDefs.CInt]
      RETURNS [processDescriptor: CLibraryDefs.CInt] = {
      
      Error["wait not implemented."L];
      RETURN [0];  };

  write: PUBLIC PROC [fileDescriptor: CLibraryDefs.FILE, buffer: CLibraryDefs.CString, nBytes: CLibraryDefs.CInt]
      RETURNS [nBytesWritten: CLibraryDefs.CInt] = {
      
      Error["write not implemented."L];
      RETURN [0];  };

  
  -- Library Functions and Procedures
  
  
  abs: PUBLIC PROC [i: CLibraryDefs.CInt]
      RETURNS [AbsI: CLibraryDefs.CInt] = {
      
      RETURN [IF i >= 0 THEN i ELSE -1];  };

  atoi:	PUBLIC PROC [numberPointer: CLibraryDefs.CString]
      RETURNS [number: CLibraryDefs.CInt] = {
      
      Error["atoi not implemented."L];
      RETURN [0];  };

  atol:	PUBLIC PROC [numberPointer: CLibraryDefs.CString]
      RETURNS [number: CLibraryDefs.CInt] = {
      
      Error["atol not implemented."L];
      RETURN [0];  };

  ctime: PUBLIC PROC [time: LONG POINTER TO CLibraryDefs.CInt]
      RETURNS [timeString: CLibraryDefs.CString] = {
      
      Error["ctime not implemented."L];
      RETURN [NIL];  };

  fclose: PUBLIC PROC [stream: CLibraryDefs.FILE] = {
  
    junk: CLibraryDefs.CInt ← close[stream];
    };
    
  
  fflush: PUBLIC PROC [stream: CLibraryDefs.FILE] = {
      
      -- do nothing for now
      };

  
  fgetc: PUBLIC PROC [stream: CLibraryDefs.FILE]
      RETURNS [char: CLibraryDefs.CChar] = {
      
      RETURN [getc[stream]];  };

  fgets: PUBLIC PROC [string: CLibraryDefs.CString, length: CLibraryDefs.CInt,
      stream: CLibraryDefs.FILE]
      RETURNS [sameString: CLibraryDefs.CString] = {
      
      RETURN [gets[string]];  };
      
    
  fopen: PUBLIC PROC [fileName, type: CLibraryDefs.CString]
      RETURNS [stream: CLibraryDefs.FILE] = {
  
    stream ← open[fileName, 644B];
    };
    
  fputc: PUBLIC PROC [char: CLibraryDefs.CChar, stream: CLibraryDefs.FILE] = {
      
      putc[char, stream];  };

  
  fputs: PUBLIC PROC [string: CLibraryDefs.CString, stream: CLibraryDefs.FILE] = {
  
    char: CLibraryDefs.CChar;
  
    DO
        char ← string↑;
	IF char = Ascii.NUL-0C THEN EXIT;
	putc[char, stream];
	string ← string + 1;
	ENDLOOP;
    };
    
  
  fprintf: PUBLIC PROC [stream: CLibraryDefs.FILE, format: CLibraryDefs.CString,
	N1: LONG UNSPECIFIED ← 17777777777B,
	N2: LONG UNSPECIFIED ← 17777777777B,
	N3: LONG UNSPECIFIED ← 17777777777B,
	N4: LONG UNSPECIFIED ← 17777777777B,
	N5: LONG UNSPECIFIED ← 17777777777B,
	N6: LONG UNSPECIFIED ← 17777777777B,
	N7: LONG UNSPECIFIED ← 17777777777B,
	N8: LONG UNSPECIFIED ← 17777777777B] = {
	
	printf[format, N1, N2, N3, N4, N5, N6, N7, N8]; };
	
  fread: PUBLIC PROC [buffer: LONG POINTER, sizeOfBufferItems,
  	  nItems: CLibraryDefs.CInt, stream: CLibraryDefs.FILE]
      RETURNS [itemsRead: CLibraryDefs.CInt] = {
      
      Error["fread not implemented."L];
      RETURN [0];  };

  free:	PUBLIC PROC [LONG POINTER] = {
      
      Error["free not implemented."L];  };

  
  freopen: PUBLIC PROC [fileName, type: LONG POINTER TO INTEGER,
  				oldStream: CLibraryDefs.FILE]
      RETURNS [newStream: CLibraryDefs.FILE] = {
      
      -- This implementation in INCORRECT.  Correct it later.
      RETURN [fopen[fileName, type]];  };

  fscanf: PUBLIC PROC [stream: CLibraryDefs.FILE, format: CLibraryDefs.CString,
          N1: LONG POINTER ← NIL,
          N2: LONG POINTER ← NIL,
          N3: LONG POINTER ← NIL,
          N4: LONG POINTER ← NIL,
          N5: LONG POINTER ← NIL,
          N6: LONG POINTER ← NIL,
          N7: LONG POINTER ← NIL,
          N8: LONG POINTER ← NIL] = {
      
      Error["fscanf not implemented."L];  };
      
  fseek: PUBLIC PROC [stream: CLibraryDefs.FILE, offset, ptrName: CLibraryDefs.CInt]
      RETURNS [itemsRead: CLibraryDefs.CInt] = {
      
      Error["fseek not implemented."L];
      RETURN [0];  };

  fwrite: PUBLIC PROC [buffer: LONG POINTER, sizeOfBufferItems,
  	  nItems: CLibraryDefs.CInt, stream: CLibraryDefs.FILE]
      RETURNS [itemsWritten: CLibraryDefs.CInt] = {
      
      Error["fwrite not implemented."L];
      RETURN [0];  };

  getc: PUBLIC PROC [stream: CLibraryDefs.FILE]
    RETURNS [char: CLibraryDefs.CChar] = {
  
    file: INTEGER ← Inline.LowHalf[stream];
  
    char ← 0;
    IF file >= UnixDefs.numOpenFiles THEN {
        Error["File number too high in getc."L];
	RETURN;  };
    SELECT UnixDefs.openFiles[file].type FROM
        unused => Error["Invalid file number in getc."L];
	stream => {
	    char ← Stream.GetChar[UnixDefs.openFiles[file].streamHandle !
	        Stream.EndOfStream => {
                    char ← -1;
                    CONTINUE};  ]-0C;  };
	window => {
	    char ← TTYSW.GetChar[UnixDefs.openFiles[file].windowHandle]-0C;
	    TTYSW.AppendChar[UnixDefs.openFiles[file].windowHandle, char + 0C];
	    IF char = 4 --Ascii.EOT-- THEN char ← -1;  };
	ENDCASE;
     };

  getchar: PUBLIC PROC []
      RETURNS [char: CLibraryDefs.CChar] = {
    
    char ← getc[0]; };
    
  gets:	PUBLIC PROC [string: CLibraryDefs.CString]
      RETURNS [sameString: CLibraryDefs.CString] = {
      
      char: CLibraryDefs.CChar;
      i: LONG INTEGER;
  
      DO
          char ← getchar[];
          IF char < 0 THEN EXIT;
          string↑ ← char;
          string ← string + 1;
          IF char = Ascii.CR-0C THEN EXIT;
          ENDLOOP;
      IF char < 0 AND i = 0 THEN RETURN[NIL];
      string↑ ← Ascii.NUL-0C;
      RETURN[string];
      };

  isalpha: PUBLIC PROC [char: CLibraryDefs.CChar]
      RETURNS [logical: CLibraryDefs.CInt] = {
      
      logical ← IF ('a-0C <= char AND char <= 'z-0C)
                OR ('A-0C <= char AND char <= 'Z-0C)
                THEN 1 ELSE 0;
      };

  isdigit: PUBLIC PROC [char: CLibraryDefs.CChar]
      RETURNS [logical: CLibraryDefs.CInt] = {
      
      logical ← IF '0-0C <= char AND char <= '9-0C
                THEN 1 ELSE 0;
      };
      
  islower: PUBLIC PROC [char: CLibraryDefs.CChar]
      RETURNS [logical: CLibraryDefs.CInt] = {
      
      logical ← IF ('a-0C <= char AND char <= 'z-0C)
                THEN 1 ELSE 0;
      };

  isupper: PUBLIC PROC [char: CLibraryDefs.CChar]
      RETURNS [logical: CLibraryDefs.CInt] = {
      
      logical ← IF ('A-0C <= char AND char <= 'Z-0C)
                THEN 1 ELSE 0;
      };

  malloc: PUBLIC PROC [size: CLibraryDefs.CInt]
      RETURNS [LONG POINTER] = {
      
      Error["malloc not implemented."L];
      RETURN [NIL];  };

  perror: PUBLIC PROC [string: CLibraryDefs.CString] = {
      
      Error["Perror: "L];
      fprintf[CLibraryDefs.stderr, string];  };

  
  printf: PUBLIC PROC [format: CLibraryDefs.CString,
	N1: LONG UNSPECIFIED ← 17777777777B,
	N2: LONG UNSPECIFIED ← 17777777777B,
	N3: LONG UNSPECIFIED ← 17777777777B,
	N4: LONG UNSPECIFIED ← 17777777777B,
	N5: LONG UNSPECIFIED ← 17777777777B,
	N6: LONG UNSPECIFIED ← 17777777777B,
	N7: LONG UNSPECIFIED ← 17777777777B,
	N8: LONG UNSPECIFIED ← 17777777777B] = {
	
  i: CARDINAL;
  outString: LONG STRING;
  items: ARRAY [1..8] OF LONG UNSPECIFIED;
  nextItem: CARDINAL ← 1;
  
  -- put the arguments into an array for easy sequential access
  items[1] ← N1; items[2] ← N2; items[3] ← N3; items[4] ← N4;
  items[5] ← N5; items[6] ← N6; items[7] ← N7; items[8] ← N8;
  
  -- scan the format string, interpreting the '% fields
  FOR i ← 0, i + 1 WHILE (format + i)↑ # 0 DO
      IF (format + i)↑ # '%-0C THEN putchar[(format + i)↑]
      ELSE {
          span: CARDINAL;
	  
	  i ← i + 1;	-- get to the character after the '%
	  
	  -- read the field width (if present)
	  IF (format + i)↑ IN ['0-0C..'9-0C] THEN {
	      span ← (format + i)↑ - ('0-0C);
	      DO
	          i ← i + 1;
		  IF (format + i)↑ NOT IN ['0-0C..'9-0C] THEN EXIT;
		  span ← 10 * span + ((format + i)↑ - ('0-0C));
	      ENDLOOP; }
	  ELSE span ← 0;
	  
	  -- skip past the "long" indicator if there is one
	  IF (format + i)↑ = 'L-0C OR (format + i)↑ = 'l-0C THEN i ← i + 1;
	  
	  outString ← Storage.CopyString[s: ""L, longer: 64];
	  SELECT (format + i)↑ FROM
	      'd-0C => String.AppendLongDecimal[outString,
	          LOOPHOLE[items[nextItem], LONG INTEGER]];
		      
	      'o-0C => String.AppendLongNumber[outString,
	          LOOPHOLE[items[nextItem], LONG INTEGER], 8];
		      
	      'c-0C => {
	          c: CHARACTER ← 0C + Inline.LowHalf[items[nextItem]];
	          Storage.AppendChar[@outString, c]; };
		  
	      's-0C => {
	          i: CARDINAL;
	          s: LONG POINTER TO INTEGER;
		  s ← LOOPHOLE[items[nextItem], LONG POINTER TO INTEGER];
		  FOR i ← 0, i+1 WHILE (s+i)↑ # 0 DO
		      Storage.AppendChar[@outString, (s+i)↑+0C];  ENDLOOP;  };
	      
	      ENDCASE =>
	          Storage.AppendString[@outString,
		      "*** printf: bad % flag ***"L];
		      
	  nextItem ← nextItem + 1;
	  FOR j: CARDINAL IN [outString.length..span) DO
	      putchar[' -0C]; ENDLOOP;
	  FOR j: CARDINAL IN [0..outString.length) DO
	      putchar[outString.text[j]-0C]; ENDLOOP; };
      ENDLOOP;
    };
  
  putc: PUBLIC PROC [char: CLibraryDefs.CChar, stream: CLibraryDefs.FILE] = {
  
    file: INTEGER ← Inline.LowHalf[stream];
  
    IF file >= UnixDefs.numOpenFiles THEN {
        Error["File number too high in putc."L];
	RETURN;  };
    SELECT UnixDefs.openFiles[file].type FROM
        unused => Error["Invalid file number in putc."L];
	stream => Stream.PutChar[UnixDefs.openFiles[file].streamHandle, char + 0C];
	window => TTYSW.AppendChar[UnixDefs.openFiles[file].windowHandle, char + 0C];
	ENDCASE;
    };
    
  
  putchar: PUBLIC PROC [char: CLibraryDefs.CChar] = {
  
    putc[char, 1]; };
    
  
  puts:	PUBLIC PROC [string: CLibraryDefs.CString, stream: CLibraryDefs.FILE] = {
      
      fputs[string, stream];
      };

  
  scanf: PUBLIC PROC [format: CLibraryDefs.CString,
     			N1: LONG POINTER ← NIL,
     			N2: LONG POINTER ← NIL,
     			N3: LONG POINTER ← NIL,
     			N4: LONG POINTER ← NIL,
     			N5: LONG POINTER ← NIL,
     			N6: LONG POINTER ← NIL,
     			N7: LONG POINTER ← NIL,
     			N8: LONG POINTER ← NIL] = {
      
      Error["scanf not implemented."L];  };

  setbuf: PUBLIC PROC [stream: CLibraryDefs.FILE, buffer: LONG POINTER] = {
      
      -- For now just do nothing.
      };

  
  strcat: PUBLIC PROC [s1, s2: CLibraryDefs.CString]
      RETURNS [result: CLibraryDefs.CString] = {
      
      Error["strcat not implemented."L];
      RETURN [NIL];  };

  strcmp: PUBLIC PROC [s1, s2: CLibraryDefs.CString]
      RETURNS [result: CLibraryDefs.CInt] = {
      
      Error["strcmp not implemented."L];
      RETURN [0];  };

  strcpy: PUBLIC PROC [s1, s2: CLibraryDefs.CString]
      RETURNS [result: CLibraryDefs.CString] = {
      
      Error["strcpy not implemented."L];
      RETURN [NIL];  };

  strlen: PUBLIC PROC [s1, s2: CLibraryDefs.CString]
      RETURNS [stringLength: CLibraryDefs.CInt] = {
      
      Error["strlen not implemented."L];
      RETURN [0];  };

  toupper: PUBLIC PROC [cin: CLibraryDefs.CChar]
      RETURNS [cout: CLibraryDefs.CChar] = {
      
      RETURN[IF ORD['a] <= cin AND cin <= ORD['z]
                THEN cin-('A-'a) ELSE cin];
      };

  
  Error:  PUBLIC PROC [s: LONG STRING] = {
  
    FOR i: CARDINAL IN [0..s.length) DO
        putc[s.text[i]-0C, 2]; ENDLOOP;
    };
    
    
  -- Artifacts of the simulation of Unix in Tajo
     
  GetArgs: PUBLIC PROC [argc: LONG POINTER TO LONG INTEGER,
    argv: LONG POINTER TO LONG POINTER TO LONG POINTER TO INTEGER] = {
    
    UnixDefs.GetArgs[argc, argv];
    };
    

}.