unit IOU;{common port for i/o operation}
{$I define.pas}
{ $I nodebug.pas}
interface
uses
  IOType, MyType, TVType, RTType
  {$IFDEF WIN32}, Classes{$ENDIF};

{minimal procedural interface:}
function IOInit(AIOName:TIOName; AMode:TIOMode; var AIO:THIO):TIOResult;{$IFDEF PMODE}export;{$ENDIF}
function IODone(var AIO:THIO):TIOResult;{$IFDEF PMODE}export;{$ENDIF}

function IOGetError(AIO:THIO):TIOError;{to get last error
  encountered, check if some method returns <> irOK}{$IFDEF PMODE}export;{$ENDIF}
function IOGetProp(AIO:THIO; AProperty:TIOProperty; AValue:pointer):TIOResult;{$IFDEF PMODE}export;{$ENDIF}

function IOSetProp(AIO:THIO; AProperty:TIOProperty; AValue:pointer):TIOResult;{$IFDEF PMODE}export;{$ENDIF}
{byte i/o:}
function IOGet(AIO:THIO; var AValue:byte):TIOResult;{$IFDEF PMODE}export;{$ENDIF}
{..usually AValue = byte, but can also contain more info, like time when the byte
  was read; if more then one byte requested, then IOSetProp must be used
  to inform that bigger buffer for AValue will be used then of size 1 (byte)}
function IOPut(AIO:THIO; AValue:byte):TIOResult;{$IFDEF PMODE}export;{$ENDIF}
{block i/o:}
function IOWrite(AIO:THIO; var Buf; Size:longint):TIOResult;{$IFDEF PMODE}export;{$ENDIF}

function IORead(AIO:THIO; var Buf; Size:longint; var ReadSize:longint):TIOResult;{$IFDEF PMODE}export;{$ENDIF}

function IOWriteLine(AIO:THIO; var Buf; Size:longint):TIOResult;{$IFDEF PMODE}export;{$ENDIF}
{.. in pascal mode (default) Buf = shortstring, writes all the shortstring chars (up to Size - 1 chars) to io;
    in C mode Buf = Pchar, writes all chars up to first 0 or max Size chars;
    appends CRLF}
function IOReadLine(AIO:THIO; var Buf; Size:longint):TIOResult;{$IFDEF PMODE}export;{$ENDIF}
{.. in pascal mode Buf = shortstring, reads up to Size - 1 chars into Buf[1..]
  or up to first crlf (in DosAsciiMode - default, or first lf - UnixAsciiMode),
  and sets Buf[0] to length; cr and lf alse read in (skipped for next ReadLine);
 .. in C mode reads in chars from Buf[0], until crlf (lf) reached or Size - 1
   reached, then 0 is appened}

function IOWriteString(AIO:THIO; AString:ShortString):TIOResult;
{$IFDEF PMODE}export;{$ENDIF}
function IOWriteStringLine(AIO:THIO; AString:ShortString):TIOResult;
{$IFDEF PMODE}export;{$ENDIF}

function IOGetPropInt(AIO:THIO; ip:TIOProperty):longint;
{$IFDEF PMODE}export;{$ENDIF}
function IOSetPropInt(AIO:THIO; ip:TIOProperty; AValue:Longint):TIOResult;
{$IFDEF PMODE}export;{$ENDIF}

function IOReset(AIO:THIO):TIOResult;
{/minimal procedural interface:}


implementation
uses
  MyLib, {itemtbl, }
  {$IFDEF WINDOWS}
  SysUtils, {Strings,}
  {$ENDIF}
  {$IFDEF USEFILES}
    {$IFNDEF WIN32}
    Streams,
    {$ENDIF}
  {$ENDIF}
  Stru
  {$IFDEF USECOMIO}
  ,rcomtype, rcom, rtu {$IFDEF USEPOLLPORT} ,pollport{$ENDIF}
  {$ENDIF}

{ $IFDEF PMODE}
  {logproc,}
{ $ELSE}
  ,LogType, ConfType
  {$IFDEF USEDLL}
    {$IFDEF USESYS}
    ,Msgu, Logu, Timer, Confu
    {$ELSE}
    ,msgproc, logproc, confproc {,guiproc}
    {$ENDIF}
  {$ELSE}
  ,msgu, logu, timer, confu
  {$ENDIF}
  {$IFDEF RTKERNEL}
  {,RTKernel}
  {$ENDIF}
{ $ENDIF}
  ;

type
  TIOInfo = record
    IOType:TIOType;{=0 if this info not in use}
    GetTimeout:TRTDuration;{rttype}
    BlockingGet:boolean;{get call wait until some data arrives for ever?
      if true GetTimeout ignored}
    {$IFDEF USECOMIO}
    CurGetData
    {LastGetData}:TComData;{is set by last call to IOGet}
    CurGetTime,
    LastGetTime:TRTDuration;{curtime - receive time of byte just read,
      CurGetTime - time of the byte recevied before current byte}
    Polling:boolean;
    {$ENDIF}
    {$IFDEF USEPOLLPORT}
    ComPort:PComPort;
    {$ENDIF}
  case word of
    0: (IOHandleB:byte);
    1: (IOHandleW:word);
    2: (IOHandleL:longint);
    3: (IOHandleP:pointer);
    4: (ComNum:TIONum);
    {$IFDEF USEFILES}
      {$IFDEF WIN32}
    5: (FileStream:TFileStream);
      {$ELSE}
    5: (FileStream:PFastBufStream);
      {$ENDIF}
    {$ENDIF}
  end;

const
  MaxIOTableSize = MaxBufSize div sizeof(TIOInfo);
  DefIOTableSize = 100;
  IOTableSize:integer = DefIOTableSize;{for maximally 100 ioports open}

{IOType:}
  itNone = 0;
  itCOM = 1;
  itFileStream = 2;
  {..}
type
  TIOTable = array[1..MaxIOTableSize] of TIOInfo;
  PIOTable = ^TIOTable;

  {IOTable = array[0..MaxIOTableSize-1] of TIOInfo;}
const
  IOTable:PIOTable = nil;


function IsValidIO(AIO:THIO):boolean;
begin
  IsValidIO := (AIO > 0) and (AIO <= IOTableSize) and (IOTable <> nil) and
    (IOTable^[AIO].IOType <> itNone);
end;

{minimal procedural interface:}
function IOInit(AIOName:TIOName; AMode:TIOMode; var AIO:THIO):TIOResult;
var
  {ioinfo:TIOInfo;}
  index:integer;
  {$IFDEF WINDOWS}
{  dest:array[0..255]of char;}
  {$ELSE}
  {$ENDIF}
  {$IFDEF USECOMIO}
  anum:integer;
  {$ENDIF}
begin
  AIO := NoHIO;
  IOInit := irNotEnoughMemory;{iotype}
  if IOTable = nil then begin
    {IOTable := ItemsTableInit(IOTableSize, sizeof(TIOInfo));}
    GetMem(IOTable, sizeof(TIOInfo) * IOTableSize);
    if IOTable = nil then
      exit;
    FillChar(IOTable^, sizeof(TIOInfo) * IOTableSize, 0);
  end;

  {FillChar(ioinfo, sizeof(ioinfo), 0);}

  for index := 1 to IOTableSize do begin
    if IOTable^[index].IOType = itNone then begin
      {$IFDEF USECOMIO}
      anum := StrToInt(copy(AIOName, 4, 2));
      if (anum > 0) and
         (length(AIOName) <= 5) and
         ('COM' = USUppCase(copy(AIOName,1,3)))
      then begin
        {$IFDEF USEPOLLPORT}
        IOTable^[index].ComPort := ComInit(anum);
        if IOTable^[index].ComPort <> nil then begin
          IOTable^[index].IOType := itCOM;
          IOTable^[index].ComNum := anum;
          IOTable^[index].Polling := true;
          aio := index;
          IOInit := irOK;

        end else
          exit;
        {$ELSE}
        if InitPortFromConfig(anum) then begin
          IOTable^[index].IOType := itCOM;
          IOTable^[index].ComNum := anum;
          IOTable^[index].Polling := GetComPropInt(IOTable^[index].ComNum, cpPolling) = 1;
          {if not ItemsTableAddItem(IOTable, ioinfo, index) then
          begin
            exit;
          end;}
          aio := index;
          IOInit := irOK;
        end else
        begin
          exit;
        end;
        {$ENDIF}

      end else
      {$ENDIF}
      begin

        {$IFDEF USEFILES}
        IOTable^[index].IOType := itFileStream;{mylib}{streams}
          {$IFDEF WIN32}
            if AMode = stCreate then
              AMode := fmCreate
            else begin
              AMode := AMode and $FF;
              {case AMode of
                stOpen: AMode := fmOpenReadWrite;
                stOpenRead: AMode := fmOpenRead;
                stOpenWrite: AMode := fmOpenWrite;
              end;}
            end;
            IOTable^[index].FileStream :=
            TFileStream.Create(AIOName, AMode);
          {$ELSE}
            IOTable^[index].FileStream :=
            New(PFastBufStream, Init(AIOName, AMode, 1024));
          {$ENDIF}

        if IOTable^[index].FileStream = nil then
          exit;
        {
        if not ItemsTableAddItem(IOTable, ioinfo, index) then
        begin
          FreeObject(@ioinfo.FileStream);
          exit;
        end;}
        aio := index;
        IOInit := irOK;{ioinfo.FileStream^.Status;}
        {$ELSE}
        SysError('Invalid IO type: ' + AIOName);
        IOInit := irInvalidIOType;{iotype}
        {$ENDIF}
      end;

      exit;
    end;
  end;
  IOInit := irTooManyIOHandles;
end;

function IOReset(AIO:THIO):TIOResult;
begin
  {$IFDEF DEBUG}
  IOReset := irInvalidHandle;
  if not IsValidIO(AIO) then
    exit;
  {$ENDIF}
  IOReset := irInvalidIOType;
  case IOTable^[AIO].IOType of
    (*
    {$IFDEF USECOMIO}
    itCOM: begin
      {$IFDEF USEPOLLPORT}
      ComDone(IOTable^[AIO].ComPort);
      {$ELSE}
      DisableCOMInterrupt(IOTable^[AIO].ComNum);
      ConfigReadWrite(rwWrite, IOTable^[AIO].ComNum);
      {$ENDIF}
     end;
    {$ENDIF}
    *)
    {$IFDEF USEFILES}
    itFileStream: begin {mylib}
      {$IFDEF WIN32}
      {IOTable^[AIO].FileStream.Seek(PLongint(AValue)^, soFromBeginning);}
      {$ELSE}
      IOTable^[AIO].FileStream^.Reset;
      {$ENDIF}
      IOReset := irOK;
    end;
    {$ENDIF}
    itNone:;
  else
    SysError('Invalid IO type')
  end;
end;

function IODone(var AIO:THIO):TIOResult;
{var
  ioinfo:TIOInfo;}
label ex;
begin
  IODone := irInvalidHandle;
  if not IsValidIO(AIO) then
    goto ex;
{  if not ItemsTableGetItemAt(IOTable, ioinfo, aio) then
    goto ex;}
  case IOTable^[AIO].IOType of
    {$IFDEF USECOMIO}
    itCOM: begin
      {$IFDEF USEPOLLPORT}
      ComDone(IOTable^[AIO].ComPort);
      {$ELSE}
      DisableCOMInterrupt(IOTable^[AIO].ComNum);
      ConfigReadWrite(rwWrite, IOTable^[AIO].ComNum);
      {$ENDIF}
     end;
    {$ENDIF}
    {$IFDEF USEFILES}
    itFileStream: begin {mylib}
      FreeObject(@IOTable^[AIO].FileStream);
    end;
    {$ENDIF}
    itNone: goto ex;
  else
    goto ex;
  end;
  FillChar(IOTable^[AIO], sizeof(TIOInfo), 0);
  {ItemsTablePutItemAt(IOTable, ioinfo, aio);}
  IODone := irOK;
ex:
  aio := NoHIO;
end;

function IOGetError(AIO:THIO):TIOError;{to get last error
  encountered, check if some method returns <> irOK}
begin
  IOGetError := irOK;
end;

function IOGetProp(AIO:THIO; AProperty:TIOProperty; AValue:pointer):TIOResult;
var
  res:TIOResult;
{  ioinfo:TIOInfo;}
begin
  res := irInvalidHandle;
  IOGetProp := res;
  if not IsValidIO(AIO) then
    exit;
{  if not ItemsTableGetItemAt(IOTable, ioinfo, aio) then
    exit;}
  res := irOK;
  case IOTable^[AIO].iotype of

    {$IFDEF USEFILES}
    itFileStream: begin

      case AProperty of
        ipStatus : begin
          {$IFDEF WIN32}
          PInteger(AValue)^ := 0;{IOTable^[AIO].FileStream^.Status;}
          {$ELSE}
          PInteger(AValue)^ := IOTable^[AIO].FileStream^.Status;
          {$ENDIF}
        end;
        ipName: begin
          {$IFDEF WIN32}
          PString(AValue)^ := 'not yet';{GetString(IOTable^[AIO].FileStream.FName);}
          {$ELSE}
          PString(AValue)^ := GetString(IOTable^[AIO].FileStream^.FName);
          {$ENDIF}
        end;

        ipEOF: begin
          {$IFDEF WIN32}
          PBoolean(AValue)^ := (IOTable^[AIO].FileStream.Size = IOTable^[AIO].FileStream.Position);
          {$ELSE}
          PBoolean(AValue)^ := IOTable^[AIO].FileStream^.EOF;
          {$ENDIF}
        end;
        ipPos: begin
          {$IFDEF WIN32}
          PLongint(AValue)^ := IOTable^[AIO].FileStream.Position;
          {$ELSE}
          PLongint(AValue)^ := IOTable^[AIO].FileStream^.GetPos;
          {$ENDIF}
        end;
        ipSize: begin
          {$IFDEF WIN32}
          PLongint(AValue)^ := IOTable^[AIO].FileStream.Size;
          {$ELSE}
          PLongint(AValue)^ := IOTable^[AIO].FileStream^.GetSize;
          {$ENDIF}
        end;
        ipBufStream: begin
          PLongint(AValue)^ := longint(IOTable^[AIO].FileStream);
        end;
      else
        begin
          res := irInvalidGetProperty;
        end;
      end;

    end;
    {$ENDIF}

    {$IFDEF USECOMIO}
    itCOM: begin
      case AProperty of
        ipStatus:;
        ipName: begin
          PString(AValue)^ := 'COM' + IntToStr(IOTable^[AIO].ComNum);
        end;
        {$IFNDEF USEPOLLPORT}
        ipInMailBox: begin
          PRTMailBox(AValue)^ := ReceiveBuffer[IOTable^[AIO].ComNum];
        end;
        ipOutMailBox: begin
          PRTMailBox(AValue)^ := SendBuffer[IOTable^[AIO].ComNum];
        end;
        ipComFifoTrigLevel: begin
          PTriggerLevel(AValue)^ :=
            GetComPropInt(IOTable^[AIO].ComNum, cpFifoTrigLevel);
        end;
        {$ENDIF}
        ipIONum: begin
          PIONum(AValue)^ := IOTable^[AIO].ComNum;
        end;
        ipGetTimeout: begin
          PRTDuration(AValue)^ := IOTable^[AIO].GetTimeout;
        end;
        ipBlockingGet: begin
          PBoolean(AValue)^ := IOTable^[AIO].BlockingGet;
        end;
        ipCurGetTime: begin
          PRTDuration(AValue)^ := IOTable^[AIO].CurGetTime;
        end;
        ipLastGetTime: begin
          PRTDuration(AValue)^ := IOTable^[AIO].LastGetTime;
        end;
      else
        res := irInvalidGetProperty;
      end;
    end;
    {$ENDIF}
    itNone: begin
      res := irInvalidIOType;
    end;
  else
    begin
      res := irInvalidIOType;
    end;
  end;

  if res = irInvalidGetProperty then
  begin
    {$IFDEF SYSLOG}
    SysError('IOGetProp: invalid property ' + IntToStr(AProperty));
    {$ENDIF}
  end;

  IOGetProp := res;
end;

function IOGetPropInt(AIO:THIO; ip:TIOProperty):longint;
var l:Longint;
begin
  l := 0;
  IOGetProp(AIO, ip, @l);
  IOGetPropInt := l;
end;

function IOSetPropInt(AIO:THIO; ip:TIOProperty; AValue:Longint):TIOResult;
begin
  IOSetPropInt := IOSetProp(AIO, ip, @AValue);
end;


function IOSetProp(AIO:THIO; AProperty:TIOProperty; AValue:pointer):TIOResult;
var
  res:TIOResult;
  {ioinfo:TIOInfo;}
begin
  res := irInvalidHandle;
  IOSetProp := res;
  if not IsValidIO(AIO) then
    exit;
  {
  if not ItemsTableGetItemAt(IOTable, ioinfo, aio) then
    exit;
  }
  res := irOK;
  case IOTable^[AIO].iotype of
    {$IFDEF USEFILES}
    itFileStream: begin

      case AProperty of
        ipGetTimeout: begin
          IOTable^[AIO].GetTimeout := PLongint(AValue)^;
          {ItemsTablePutItemAt(IOTable, ioinfo, aio)}
        end;
        ipBlockingGet: begin
          IOTable^[AIO].BlockingGet := PBoolean(AValue)^;
          {ItemsTablePutItemAt(IOTable, ioinfo, aio)}
        end;
        ipPos: begin
          {$IFDEF WIN32}
          IOTable^[AIO].FileStream.Seek(PLongint(AValue)^, soFromBeginning);
          {$ELSE}
          IOTable^[AIO].FileStream^.Seek(PLongint(AValue)^);
          {$ENDIF}
        end;
        ipFlushAlways: begin
          {$IFNDEF WIN32}
          IOTable^[AIO].FileStream^.FUseHardFlush := PBoolean(AValue)^;
          {$ENDIF}
        end;
        ipTruncate:begin
          {$IFDEF WIN32}
          IOTable^[AIO].FileStream.Size := IOTable^[AIO].FileStream.Position;
          {$ELSE}
          IOTable^[AIO].FileStream^.Truncate;
          {$ENDIF}
        end;
        ipFlush: begin
          {$IFNDEF WIN32}
          IOTable^[AIO].FileStream^.Flush;
          {$ENDIF}
        end;
      else
        begin
          res := irInvalidSetProperty;
        end;
      end;

    end;
    {$ENDIF}

    {$IFDEF USECOMIO}
    itCOM: begin
      case AProperty of
        ipGetTimeout: begin
          IOTable^[AIO].GetTimeout := PLongint(AValue)^;
          {ItemsTablePutItemAt(IOTable, ioinfo, aio)}
        end;
        ipBlockingGet: begin
          IOTable^[AIO].BlockingGet := PBoolean(AValue)^;
          {ItemsTablePutItemAt(IOTable, ioinfo, aio)}
        end;
        {$IFNDEF USEPOLLPORT}
        ipComFifoTrigLevel: begin
          if HasFifo(IOTable^[AIO].ComNum) then
            EnableFifo(IOTable^[AIO].ComNum, PTriggerLevel(AValue)^);
        end;
        {$ENDIF}
      else
        res := irInvalidSetProperty;
      end;
    end;
    {$ENDIF}
    itNone: begin
      res := irInvalidIOType;
    end;
  else
    begin
      res := irInvalidIOType;
    end;
  end;

  if res = irInvalidSetProperty then
  begin
    {$IFDEF SYSLOG}
    SysError('IOSetProp: invalid property ' + IntToStr(AProperty));
    {$ENDIF}
  end;

  IOSetProp := res;
end;

{byte i/o:}
function IOGet(AIO:THIO; var AValue:byte):TIOResult;
var
  res:TIOResult;
  valu:byte absolute AValue;

  {$IFDEF USECOMIO}
  function GetComPort:TIOResult;
  var
    Success:boolean;

    {$IFDEF USEPOLLPORT}
    procedure GetPolled;
    var i:integer;
    begin
      i := ComGetWait(IOTable^[AIO].ComPort, IOTable^[AIO].GetTimeout);
      if i < 0 then begin
        Success := false;
      end else begin
        Success := true;
        AValue := i;
      end;
    end;
    {$ELSE}
    procedure GetPolled;
    begin
      if IOTable^[AIO].BlockingGet then begin
        ReceiveCharPolled(IOTable^[AIO].ComNum, IOTable^[AIO].CurGetData,
          -1, Success);
        AValue := ord(IOTable^[AIO].CurGetData.Ch);
      end else begin
        ReceiveCharPolled(IOTable^[AIO].ComNum, IOTable^[AIO].CurGetData,
          IOTable^[AIO].GetTimeout, Success);
        AValue := ord(IOTable^[AIO].CurGetData.Ch);
      end;
    end;
    {$ENDIF}

    procedure GetInterrupt;
    begin
      {interrupt:}
      if IOTable^[AIO].BlockingGet then begin
        RTGet(ReceiveBuffer[IOTable^[AIO].ComNum], IOTable^[AIO].CurGetData);
        AValue := ord(IOTable^[AIO].CurGetData.Ch);
      end else begin
        if IOTable^[AIO].GetTimeout <> 0 then begin
          RTGetTimed(ReceiveBuffer[IOTable^[AIO].ComNum], IOTable^[AIO].CurGetData,
            IOTable^[AIO].GetTimeout, Success);
          AValue := ord(IOTable^[AIO].CurGetData.Ch);
        end else begin
          RTGetCond(ReceiveBuffer[IOTable^[AIO].ComNum], IOTable^[AIO].CurGetData, Success);
          AValue := ord(IOTable^[AIO].CurGetData.Ch);
        end;
      end;
    end;

  begin
    if IOTable^[AIO].Polling then
      GetPolled
    else
      GetInterrupt;
    if Success then
    begin
      with IOTable^[AIO] do begin
        LastGetTime := CurGetTime;
        CurGetTime := mstime;
        {LastGetData := CurGetData;}
      end;
      GetComPort := irOK;
    end else begin
      GetComPort := irCOMReceiveBufferEmpty;
    end;
  end;
  {$ENDIF}

  {$IFDEF USEFILES}
  function GetFileStream:TIOResult;
  var
    res:TIOResult;
    {$IFDEF WIN32}
    cnt:longint;
    {$ENDIF}
  begin
    {$IFDEF WIN32}
    cnt := IOTable^[AIO].FileStream.Read(AValue, 1);
    if cnt = 1 then
      res := irOK
    else
      res := irDiskReadError;
      {res := irAccessDenied;}
    {$ELSE}
    IOTable^[AIO].FileStream^.Read(AValue, 1);
    case IOTable^[AIO].FileStream^.Status of
      stOK: res := irOK;
      stReadError: begin
        res := irDiskReadError;
      end;
    else
      res := irAccessDenied;
    end;
    {$ENDIF}
    GetFileStream := res;
  end;
  {$ENDIF}

begin
  res := irInvalidHandle;
  IOGet := res;
  if not IsValidIO(AIO) then
    exit;
  {if not ItemsTableGetItemAt(IOTable, ioinfo, aio) then
    exit;}
  case IOTable^[AIO].IOType of
    {$IFDEF USECOMIO}
    itCOM: res := GetComPort;
    {$ENDIF}
    {$IFDEF USEFILES}
    itFileStream : res := GetFileStream;
    {$ENDIF}
    itNone: res := irInvalidIOType;
  end;
  IOGet := res;
end;

function IOPut(AIO:THIO; AValue:byte):TIOResult;
var
{  ioinfo:TIOInfo;}
  res:TIOResult;
  valu:byte absolute AValue;

  {$IFDEF USECOMIO}
    {$IFDEF USEPOLLPORT}
    function PutComPort:TIOResult;
    begin
      ComPut(IOTable^[AIO].ComPort, AValue);
      PutComPort := irOK;
    end;
    {$ELSE}
    function PutComPort:TIOResult;
    var
      Data: rcomtype .TComData;
      Success:boolean;
    begin
      if IOTable^[AIO].Polling then begin
        RCom.SendCharPolled(IOTable^[AIO].ComNum, char(AValue));
      end else begin
        RCom .SendChar(IOTable^[AIO].ComNum, chr(valu));
      end;
      PutComPort := irOK;
    end;
    {$ENDIF}
  {$ENDIF}

  {$IFDEF USEFILES}
  function PutFileStream:TIOResult;
  var
    res:TIOResult;
    {$IFDEF WIN32}
    cnt:longint;
    {$ENDIF}
  begin
    {$IFDEF WIN32}
    cnt := IOTable^[AIO].FileStream.Write(valu, 1);
    if cnt = 1 then
      res := irOK
    else
       res := irDiskWriteError;
    {res := irAccessDenied;}
    {$ELSE}
    IOTable^[AIO].FileStream^.Write(valu, 1);
    case IOTable^[AIO].FileStream^.Status of
      stOK : res := irOK;
      stWriteError: res := irDiskWriteError;
    else
      res := irAccessDenied;
    end;
    {$ENDIF}
    PutFileStream := res;
  end;
  {$ENDIF}

begin
  res := irInvalidHandle;
  IOPut := res;
  if not IsValidIO(AIO) then
    exit;
{
  if not ItemsTableGetItemAt(IOTable, ioinfo, aio) then
    exit;
}
  case IOTable^[AIO].IOType of
    {$IFDEF USECOMIO}
    itCOM: res := PutComPort;
    {$ENDIF}
    {$IFDEF USEFILES}
    itFileStream : res := PutFileStream;
    {$ENDIF}
    itNone: res := irInvalidIOType;
  end;
  IOPut := res;
end;

{block i/o:}
function IOWrite(AIO:THIO; var Buf; Size:longint):TIOResult;
var
{  ioinfo:TIOInfo;}
  bbuf:TByteBuffer absolute Buf;

  {$IFDEF USEFILES}
  function WriteFileStream:TIOResult;
  var
    res:TIOResult;
    {$IFDEF WIN32}
    cnt:longint;
    {$ENDIF}
  begin
    {$IFDEF WIN32}
    cnt := IOTable^[AIO].FileStream.Write(Buf, Size);
    if cnt = Size then
      res := irOK
    else
      res := irDiskWriteError;
      {res := irAccessDenied;}
    {$ELSE}
    if Size > MaxWord then begin
      Size := MaxWord;
      {$IFDEF SYSLOG}
      SysError('WriteFileStream: size too big ' + IntToStr(Size));
      {$ENDIF}
    end;
    IOTable^[AIO].FileStream^.Write(Buf, Size);
    case IOTable^[AIO].FileStream^.Status of
      stOK: res := irOK;
      stWriteError : res := irDiskWriteError;
    else
      res := irAccessDenied;
    end;
    {$ENDIF}

    WriteFileStream:=res;
  end;
  {$ENDIF}

  {$IFDEF USECOMIO}
  function WriteComPort:TIOResult;
  var
    res:TIOResult;
    p:longint;
  begin
    if Size > MaxWord then begin
      {$IFDEF SYSLOG}
      SysError('WriteComPort: size too big - '+ IntToStr(Size));
      {$ENDIF}
      Size := MaxWord;
    end;
    p := 0;
    res := irOK;
    while (p < Size) and (res = irOK) do begin
      res := IOPut(AIO, bbuf[p]);
      inc(p);
    end;
    WriteComPort := res;
  end;
  {$ENDIF}
begin
  IOWrite := irInvalidHandle;
  if not IsValidIO(aio) then
    exit;
{  if not ItemsTableGetItemAt(IOTable, ioinfo, aio) then
    exit;}

  case IOTable^[AIO].iotype of
    {$IFDEF USECOMIO}
    itCOM: IOWrite := WriteComPort;
    {$ENDIF}
    {$IFDEF USEFILES}
    itFileStream: IOWrite := WriteFileStream;
    {$ENDIF}
    itNone: IOWrite := irInvalidIOType;
  else
    begin
      {$IFDEF SYSLOG}
      SysError('IOWrite: invalid iotype - ' + inttostr(IOTable^[AIO].iotype));
      {$ENDIF}
      exit;
    end;
  end;
end;

function IORead(AIO:THIO; var Buf; Size:longint; var ReadSize:longint):TIOResult;
var
{  ioinfo:tioinfo;}
  res:TIOResult;
  bbuf:TByteBuffer absolute Buf;

  {$IFDEF USEFILES}
  function ReadFileStream:TIOResult;
  var
    {$IFNDEF WIN32}
    opos:longint;
    stat:integer;
    {$ENDIF}
    res:TIOResult;
  begin
    {$IFDEF WIN32}
     ReadSize := IOTable^[AIO].FileStream.Read(Buf, Size);
     {if ReadSize = Size then}
     res := irOK;
     {else}

    {$ELSE}
    opos := IOTable^[AIO].FileStream^.FilPos;{streams}
    IOTable^[AIO].FileStream^.Read(Buf, Size);
    stat := IOTable^[AIO].FileStream^.Status;
    case stat of
      stReadError: begin
        ReadSize := IOTable^[AIO].FileStream^.FilPos - opos;
        res := irOK;
      end;
      stError: begin
        res := irAccessDenied;
      end;
    else
      begin
        ReadSize := Size;
        res := irOK;
      end;
    end;
    {$ENDIF}
    ReadFileStream := res;
  end;
  {$ENDIF}

  {$IFDEF USECOMIO}
  function ReadComPort:TIOResult;
  var
    res:TIOResult;
    b:byte;
  begin
    res := irOK;
    while (ReadSize < Size) do begin
      res := IOGet(AIO, b);
      if res = irOK then begin
        inc(ReadSize);
      end else
        break;
    end;
    ReadComPort := res;
  end;
  {$ENDIF}

begin
  IORead := irInvalidHandle;
  ReadSize := 0;
  if not IsValidIO(AIO) then
    exit;
{  if not ItemsTableGetItemAt(IOTable, ioinfo, aio) then
    exit;}
  if Size > MaxWord then begin
    {$IFDEF SYSLOG}
    SysError('IORead: size too big - '+IntToStr(Size));
    {$ENDIF}
    Size := Maxword;
  end;

  case IOTable^[AIO].iotype of
    {$IFDEF USECOMIO}
    itCOM: res := ReadComPort;
    {$ENDIF}
    {$IFDEF USEFILES}
    itFileStream: res := ReadFileStream;
    {$ENDIF}
    itNone: res := irInvalidIOType;
  else
    begin
      res := irInvalidIOType;
    end;
  end;
  IORead := res;
end;


const
  crlf:string[2] = #13#10;
function IOWriteLine(AIO:THIO; var Buf; Size:longint):TIOResult;
var
  s:shortstring absolute Buf;
  l:byte;
begin
{pascal string mode:}
  l := length(s);
  if Size < l then
    l := Size;
  IOWrite(AIO, s[1], l);
  IOWriteLine := IOWrite(AIO, crlf[1], 2);
end;
{.. in pascal mode (default) Buf = string, writes all the string (up to Size chars) to io;
    in C mode Buf = Pchar, writes all chars up to first 0 or max Size chars}

function IOReadLine(AIO:THIO; var Buf; Size:longint):TIOResult;
var
  res:TIOResult;
  s:shortstring absolute Buf;
  b:byte;
  i:integer;
begin
{ res := irInvalidHandle; }
{ pascal mode: }
  if Size > 255 then
    Size := 255;
  s := '';
  i := 0;
  repeat
    res := IOGet(AIO, b);

    if res = irOK then begin
      inc(i);
      if length(s) >= size - 1 then
        break;
      case b of
        13:;
        10:break;
      else
        s := s + chr(b);
      end;
      if i > size then
        break;{prevent endless loop for lot off #13 without #10}
    end else
      break;

  until false;
  if i <> 0 then begin
    res := irOK;
  end;
  IOReadLine := res;
end;

function IOWriteString(AIO:THIO; AString:shortString):TIOResult;
begin
  IOWriteString := IOWrite(AIO, AString[1], length(AString));
end;

function IOWriteStringLine(AIO:THIO; AString:ShortString):TIOResult;
begin
  IOWriteStringLine := IOWriteLine(AIO, AString, sizeof(AString));
end;


{/minimal procedural interface:}

var
  SavedExit:pointer;

procedure ExitChain;far;
var i,j:integer;
begin
  System.ExitProc := SavedExit;
  if IOTable <> nil then begin
    for i := 1 to IOTableSize do begin
      if IOTable^[i].IOType <> itNone then begin
        j := i;
        IODone(j);
      end;
    end;
    FreeMem(IOTable, IOTableSize);
    IOTable := nil;
  end;
end;

procedure Init;
begin
  SavedExit := System.ExitProc;
  System.ExitProc := @ExitChain;
end;

begin
  Init;
end.
