{ MODULE 4 UNIX i/o Support This module provides a low-level interface to the UNIX file sys- tem. Whilst the module contains procedures for both reading and writing, in practice, only the read procedures are currently used. The details of a file are represented by the type UnixFile, whose fields include an i/o buffer, a descriptor, and a file mode flag. Files can be opened for reading or writing, but not both. To minimise disk transfers, i/o is performed on a block-by-block basis. The block-size is in turn determined by the disk format, and in this implementation is 512 bytes. On input, the buffer is truncated to an integral number of physi- cal lines. To compensate, the file read pointer is repositioned by a call to the system routine, lseek, which ensures that the dis- carded portion will be input by the next call to read. When end of file is reached, the read routine returns a zero amount, and the flag EofFlag is set true in this case. No futher input is possible once this state has been reached. To enable the checker to work as a filter, the procedures Reset- File and RewriteFile respectively check their argument names against the standard streams 'stdin' and 'stdout'. } program FileIO; #include "globals.x" #include "source.pf" function access(var Name: ArgString; Mode: integer): integer; extern; function open(var Name: ArgString; Mode: integer): integer; extern; function read(Descriptor: integer; var Buffer: FileBuffer; NBytes: integer): integer; extern; function write(Descriptor: integer; var Buffer: FileBuffer; NBytes: integer): integer; extern; function lseek(Descriptor: integer; Offset: integer; Whence: integer): integer; extern; function close(Descriptor: integer): integer; extern; procedure Terminate (var String: ArgString); visible; var i: ArgIndex; begin i := 1; while String[i] <> ' ' do i := i + 1; String[i] := chr(Null) end { Terminate }; function HeadsMatch(var Name: ArgString; Prefix: packed array[l..u: integer] of char): Boolean; visible; label 1; var i: integer; begin HeadsMatch := false; if u > MaxArgString then goto 1; for i := l to u do if Name[i] <> Prefix[i] then goto 1; HeadsMatch := true; 1: end { HeadsMatch }; function Accessible (var FileName: ArgString; Mode: FileMode): Boolean; visible; var Result: integer; begin case Mode of ForReading : Result := access(FileName, 4); ForWriting : Result := access(FileName, 2); ForBoth : Result := access(FileName, 6) end; Accessible := (Result = 0) end { Accessible }; procedure ResetFile (var TheFile: UnixFile; var FileName: ArgString); visible; begin with TheFile do begin if not HeadsMatch(FileName, 'stdin') then begin Terminate(FileName); if not Accessible(FileName, ForReading) then FatalError(1); Descriptor := open(FileName, ord(ForReading)) end else Descriptor := 0; FileAddress := 0; BufferIndex := 1; StartOfLine := 1; EndOfLine := 1; EndOfBuffer := 1; Mode := ForReading; EofFlag := false end end { ResetFile }; procedure RewriteFile (var TheFile: UnixFile; var FileName: ArgString); visible; begin with TheFile do begin if not HeadsMatch(FileName, 'stdout') then begin Terminate(FileName); if not Accessible(FileName, ForWriting) then FatalError(2); Descriptor := open(FileName, ord(ForWriting)) end else Descriptor := 1; FileAddress := 0; BufferIndex := 1; StartOfLine := 1; EndOfLine := 1; EndOfBuffer := BufferSize; Mode := ForWriting; EofFlag := true end end { RewriteFile }; procedure ReadFileBuffer(var TheFile: UnixFile); var Amount: integer; begin with TheFile do if not EofFlag then begin Amount := read(Descriptor, Buffer, BufferSize); if Amount > 0 then begin EndOfBuffer := Amount; while Buffer[EndOfBuffer] <> chr(NewLine) do EndOfBuffer := EndOfBuffer - 1; FileAddress := lseek(Descriptor,FileAddress + EndOfBuffer,0); StartOfLine := 1 end else EofFlag := true end end { ReadBuffer }; procedure ReadFileLine(var TheFile: UnixFile); visible; begin with TheFile do if not EofFlag then begin if EndOfLine = EndOfBuffer then ReadFileBuffer(TheFile) else StartOfLine := EndOfLine + 1 end end { ReadFileLine }; procedure WriteFileBuffer (var TheFile: UnixFile); var Amount, Flag: integer; begin with TheFile do begin if BufferIndex = EndOfBuffer then Amount := EndOfBuffer else Amount := BufferIndex - 1; Flag := write(Descriptor, Buffer, Amount); BufferIndex := 1 end end { WriteBuffer }; procedure WriteCh (var TheFile: UnixFile; Ch: char); visible; begin with TheFile do begin Buffer[BufferIndex] := Ch; if BufferIndex = EndOfBuffer then WriteFileBuffer(TheFile) else BufferIndex := BufferIndex + 1 end end { WriteCh }; procedure WriteSp (var TheFile: UnixFile; Count: Scalar); visible; begin while Count > 0 do begin WriteCh(TheFile, ' '); Count := Count - 1 end end { WriteSp }; procedure WriteFileLine (var TheFile: UnixFile; Count: Scalar); visible; begin while Count > 0 do begin WriteCh(TheFile, chr(NewLine)); Count := Count - 1 end end { WriteFileLine }; function EndOfFile (var TheFile: UnixFile): Boolean; visible; begin EndOfFile := TheFile.EofFlag end; procedure CloseFile (var TheFile: UnixFile); visible; var Flag: integer; begin with TheFile do begin if Mode = ForWriting then begin WriteCh(TheFile, chr(NewLine)); WriteFileBuffer(TheFile) end; Flag := close(Descriptor) end end { CloseFile }; begin { end of module } end.