{
    This file is part of the Free Pascal Run time library.
    Copyright (c) 1999-2000 by the Free Pascal development team

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}


{****************************************************************************
                    subroutines For TextFile handling
****************************************************************************}

Procedure FileCloseFunc(Var t:TextRec);
Begin
  Do_Close(t.Handle);
  t.Handle:=UnusedHandle;
End;

Procedure FileReadFunc(var t:TextRec);
Begin
  t.BufEnd:=Do_Read(t.Handle,t.Bufptr,t.BufSize);
  t.BufPos:=0;
End;


Procedure FileWriteFunc(var t:TextRec);
var
  i : longint;
Begin
  { prevent unecessary system call }
  if t.BufPos=0 then
    exit;
  i:=Do_Write(t.Handle,t.Bufptr,t.BufPos);
  if i<>t.BufPos then
    InOutRes:=101;
  t.BufPos:=0;
End;


Procedure FileOpenFunc(var t:TextRec);
var
  Flags : Longint;
Begin
  Case t.mode Of
    fmInput : Flags:=$10000;
    fmOutput : Flags:=$11001;
    fmAppend : Flags:=$10101;
  else
   begin
     InOutRes:=102;
     exit;
   end;
  End;
  Do_Open(t,PFileTextRecChar(@t.Name),Flags,False);
  t.CloseFunc:=@FileCloseFunc;
  t.FlushFunc:=nil;
  if t.Mode=fmInput then
   t.InOutFunc:=@FileReadFunc
  else
   begin
     t.InOutFunc:=@FileWriteFunc;
     { Only install flushing if its a NOT a file, and only check if there
       was no error opening the file, because else we always get a bad
       file handle error 6 (PFV) }
     if (InOutRes=0) and
        Do_Isdevice(t.Handle) then
      t.FlushFunc:=@FileWriteFunc;
   end;
End;

Procedure InitText(Var t : Text);

begin
  FillChar(t,SizeOf(TextRec),0);
{ only set things that are not zero }
  TextRec(t).Handle:=UnusedHandle;
  TextRec(t).mode:=fmClosed;
  TextRec(t).BufSize:=TextRecBufSize;
  TextRec(t).Bufptr:=@TextRec(t).Buffer;
  TextRec(t).OpenFunc:=@FileOpenFunc;
  Case DefaultTextLineBreakStyle Of
    tlbsLF: TextRec(t).LineEnd := #10;
    tlbsCRLF: TextRec(t).LineEnd := #13#10;
    tlbsCR: TextRec(t).LineEnd := #13;
  End;
end;


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
Procedure Assign(out t:Text;const s : UnicodeString);
begin
  InitText(t);
{$ifdef FPC_ANSI_TEXTFILEREC}
  TextRec(t).Name:=ToSingleByteFileSystemEncodedFileName(S);
{$else FPC_ANSI_TEXTFILEREC}
  TextRec(t).Name:=S;
{$endif FPC_ANSI_TEXTFILEREC}
  { null terminate, since the name array is regularly used as p(wide)char }
  TextRec(t).Name[high(TextRec(t).Name)]:=#0;
end;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}


{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
Procedure Assign(out t:Text;const s: RawByteString);
Begin
  InitText(t);
{$ifdef FPC_ANSI_TEXTFILEREC}
  { ensure the characters in the record's filename are encoded correctly }
  TextRec(t).Name:=ToSingleByteFileSystemEncodedFileName(S);
{$else FPC_ANSI_TEXTFILEREC}
  TextRec(t).Name:=S;
{$endif FPC_ANSI_TEXTFILEREC}
  { null terminate, since the name array is regularly used as p(wide)char }
  TextRec(t).Name[high(TextRec(t).Name)]:=#0;
End;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}


Procedure Assign(out t:Text;const s: ShortString);
Begin
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
  Assign(t,AnsiString(s));
{$else FPC_HAS_FEATURE_ANSISTRINGS}
  InitText(t);
  { warning: no encoding support }
  TextRec(t).Name:=s;
  { null terminate, since the name array is regularly used as p(wide)char }
  TextRec(t).Name[high(TextRec(t).Name)]:=#0;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}
End;


Procedure Assign(out t:Text;const p: PAnsiChar);
Begin
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
  Assign(t,AnsiString(p));
{$else FPC_HAS_FEATURE_ANSISTRINGS}
  { no use in making this the one that does the work, since the name field is
    limited to 255 characters anyway }
  Assign(t,strpas(p));
{$endif FPC_HAS_FEATURE_ANSISTRINGS}
End;


Procedure Assign(out t:Text;const c: AnsiChar);
Begin
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
  Assign(t,AnsiString(c));
{$else FPC_HAS_FEATURE_ANSISTRINGS}
  Assign(t,ShortString(c));
{$endif FPC_HAS_FEATURE_ANSISTRINGS}
End;


Procedure Close(var t : Text);[IOCheck];
Begin
  if InOutRes<>0 then
   Exit;
  case TextRec(t).mode of
    fmInput,fmOutput,fmAppend:
      Begin
        { Write pending buffer }
        If Textrec(t).Mode=fmoutput then
          FileFunc(TextRec(t).InOutFunc)(TextRec(t));
{$ifdef FPC_HAS_FEATURE_CONSOLEIO}
        { Only close functions not connected to stdout.}
        If ((TextRec(t).Handle<>StdInputHandle) and
            (TextRec(t).Handle<>StdOutputHandle) and
            (TextRec(t).Handle<>StdErrorHandle)) Then
{$endif FPC_HAS_FEATURE_CONSOLEIO}
          FileFunc(TextRec(t).CloseFunc)(TextRec(t));
        TextRec(t).mode := fmClosed;
        { Reset buffer for safety }
        TextRec(t).BufPos:=0;
        TextRec(t).BufEnd:=0;
      End
    else inOutRes := 103;
  End;
End;


Procedure OpenText(var t : Text;mode,defHdl:Longint);
Begin
  Case TextRec(t).mode Of {This gives the fastest code}
   fmInput,fmOutput,fmInOut : Close(t);
   fmClosed : ;
  else
   Begin
     InOutRes:=102;
     exit;
   End;
  End;
  TextRec(t).mode:=mode;
  TextRec(t).bufpos:=0;
  TextRec(t).bufend:=0;

{$ifdef FPC_HAS_CPSTRING}
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
  { if no codepage is yet assigned then assign default ansi codepage }
  TextRec(t).CodePage:=TranslatePlaceholderCP(TextRec(t).CodePage);
{$else FPC_HAS_FEATURE_ANSISTRINGS}
  TextRec(t).CodePage:=0;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}
{$endif FPC_HAS_CPSTRING}
  FileFunc(TextRec(t).OpenFunc)(TextRec(t));
  { reset the mode to closed when an error has occurred }
  if InOutRes<>0 then
   TextRec(t).mode:=fmClosed;
End;


Procedure Rewrite(var t : Text);[IOCheck];
Begin
  If InOutRes<>0 then
   exit;
  OpenText(t,fmOutput,1);
End;


Procedure Reset(var t : Text);[IOCheck];
Begin
  If InOutRes<>0 then
   exit;
  OpenText(t,fmInput,0);
End;


Procedure Append(var t : Text);[IOCheck];
Begin
  If InOutRes<>0 then
   exit;
  OpenText(t,fmAppend,1);
End;


Procedure Flush(var t : Text);[IOCheck];
Begin
  If InOutRes<>0 then
   exit;
  if TextRec(t).mode<>fmOutput then
   begin
     if TextRec(t).mode=fmInput then
      InOutRes:=105
     else
      InOutRes:=103;
     exit;
   end;
{ Not the flushfunc but the inoutfunc should be used, because that
  writes the data, flushfunc doesn't need to be assigned }
  FileFunc(TextRec(t).InOutFunc)(TextRec(t));
End;


Procedure Erase(var t:Text);[IOCheck];
Begin
  if InOutRes<>0 then
    exit;
  if TextRec(t).mode<>fmClosed then
    begin
      InOutRes:=102;
      exit;
    end;
  Do_Erase(PFileTextRecChar(@TextRec(t).Name),false);
End;


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
Procedure Rename(var t : Text;const s : unicodestring);[IOCheck];
{$ifdef FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
var
  fs: RawByteString;
{$endif FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
Begin
  if InOutRes<>0 then
    exit;
  if TextRec(t).mode<>fmClosed then
    begin
      InOutRes:=102;
      exit;
    end;
{$ifdef FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
  { it's slightly faster to convert the unicodestring here to rawbytestring
    than doing it in do_rename(), because here we still know the length }
  fs:=ToSingleByteFileSystemEncodedFileName(s);
  Do_Rename(PFileTextRecChar(@TextRec(t).Name),PAnsiChar(fs),false,true);
  If InOutRes=0 then
     TextRec(t).Name:=fs
{$else FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
  Do_Rename(PFileTextRecChar(@TextRec(t).Name),PUnicodeChar(S),false,false);
  If InOutRes=0 then
{$ifdef FPC_ANSI_TEXTTextRec}
    TextRec(t).Name:=ToSingleByteFileSystemEncodedFileName(s);
{$else FPC_ANSI_TEXTFILEREC}
    TextRec(t).Name:=s
{$endif FPC_ANSI_TEXTFILEREC}
{$endif FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
End;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}



{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
Procedure Rename(var t : Text;const s : rawbytestring);[IOCheck];
var
{$ifdef FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
  fs: RawByteString;
  pdst: PAnsiChar;
{$else FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
  fs: UnicodeString;
  pdst: PUnicodeChar;
{$endif FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
  dstchangeable: boolean;
Begin
  if InOutRes<>0 then
    exit;
  if TextRec(t).mode<>fmClosed then
    begin
      InOutRes:=102;
      exit;
    end;
{$ifdef FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
  dstchangeable:=false;
  pdst:=PAnsiChar(s);
  if TranslatePlaceholderCP(StringCodePage(s))<>DefaultFileSystemCodePage then
    begin
      fs:=ToSingleByteFileSystemEncodedFileName(s);
      pdst:=PAnsiChar(fs);
      dstchangeable:=true;
    end
  else
    fs:=s;
{$else FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
   { it's slightly faster to convert the rawbytestring here to unicodestring
     than doing it in do_rename, because here we still know the length }
   fs:=unicodestring(s);
   pdst:=PUnicodeChar(fs);
   dstchangeable:=true;
{$endif FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
  Do_Rename(PFileTextRecChar(@TextRec(t).Name),pdst,false,dstchangeable);
  If InOutRes=0 then
{$if defined(FPC_ANSI_TEXTTextRec) and not defined(FPCRTL_FILESYSTEM_SINGLE_BYTE_API)}
    TextRec(t).Name:=ToSingleByteFileSystemEncodedFileName(fs)
{$else FPC_ANSI_TEXTTextRec and not FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
    TextRec(t).Name:=fs
{$endif FPC_ANSI_TEXTTextRec and not FPCRTL_FILESYSTEM_SINGLE_BYTE_API}
End;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}


Procedure Rename(var t : Text;const s : ShortString);[IOCheck];
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
Begin
  Rename(t,AnsiString(s));
End;
{$else FPC_HAS_FEATURE_ANSISTRINGS}
var
  p : array[0..255] Of Char;
Begin
  Move(s[1],p,Length(s));
  p[Length(s)]:=#0;
  Rename(t,Pchar(@p));
End;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}

Procedure Rename(var t:Text;const p:PAnsiChar);
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
Begin
  Rename(t,AnsiString(p));
End;
{$else FPC_HAS_FEATURE_ANSISTRINGS}
var
  len: SizeInt;
Begin
  if InOutRes<>0 then
    exit;
  if TextRec(t).mode<>fmClosed then
    begin
      InOutRes:=102;
      exit;
    end;
  Do_Rename(PFileTextRecChar(@TextRec(t).Name),p,false,false);
  { check error code of do_rename }
  if InOutRes=0 then
    begin
      len:=min(StrLen(p),high(TextRec(t).Name));
      Move(p^,TextRec(t).Name,len);
      TextRec(t).Name[len]:=#0;
    end;
End;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}

Procedure Rename(var t : Text;const c : AnsiChar);[IOCheck];
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
Begin
  Rename(t,AnsiString(c));
End;
{$else FPC_HAS_FEATURE_ANSISTRINGS}
var
  p : array[0..1] Of AnsiChar;
Begin
  p[0]:=c;
  p[1]:=#0;
  Rename(t,PAnsiChar(@p));
End;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}

Function Eof(Var t: Text): Boolean;[IOCheck];
Begin
  If (InOutRes<>0) then
   exit(true);
  if (TextRec(t).mode<>fmInput) Then
   begin
     if TextRec(t).mode=fmOutput then
      InOutRes:=104
     else
      InOutRes:=103;
     exit(true);
   end;
  If TextRec(t).BufPos>=TextRec(t).BufEnd Then
   begin
     FileFunc(TextRec(t).InOutFunc)(TextRec(t));
     If TextRec(t).BufPos>=TextRec(t).BufEnd Then
      exit(true);
   end;
  Eof:=CtrlZMarksEOF and (TextRec(t).Bufptr^[TextRec(t).BufPos]=#26);
end;


Function Eof:Boolean;
Begin
  Eof:=Eof(Input);
End;


Function SeekEof (Var t : Text) : Boolean;
var
  oldfilepos : Int64;
  oldbufpos, oldbufend : SizeInt;
  reads: longint;
  isdevice: boolean;
Begin
  If (InOutRes<>0) then
   exit(true);
  if (TextRec(t).mode<>fmInput) Then
   begin
     if TextRec(t).mode=fmOutPut then
      InOutRes:=104
     else
      InOutRes:=103;
     exit(true);
   end;
  { try to save the current position in the file, seekeof() should not move }
  { the current file position (JM)                                          }
  oldbufpos := TextRec(t).BufPos;
  oldbufend := TextRec(t).BufEnd;
  reads := 0;
  oldfilepos := -1;
  isdevice := Do_IsDevice(TextRec(t).handle);
  repeat
    If TextRec(t).BufPos>=TextRec(t).BufEnd Then
     begin
       { signal that the we will have to do a seek }
       inc(reads);
       if not isdevice and
          (reads = 1) then
         begin
           oldfilepos := Do_FilePos(TextRec(t).handle) - TextRec(t).BufEnd;
           InOutRes:=0;
         end;
       FileFunc(TextRec(t).InOutFunc)(TextRec(t));
       If TextRec(t).BufPos>=TextRec(t).BufEnd Then
        begin
          { if we only did a read in which we didn't read anything, the }
          { old buffer is still valid and we can simply restore the     }
          { pointers (JM)                                               }
          dec(reads);
          SeekEof := true;
          break;
        end;
     end;
    case TextRec(t).Bufptr^[TextRec(t).BufPos] of
      #26 :
        if CtrlZMarksEOF then
          begin
            SeekEof := true;
            break;
          end;
     #10,#13,#9,' ' :
       ;
    else
     begin
       SeekEof := false;
       break;
     end;
    end;
   inc(TextRec(t).BufPos);
  until false;
  { restore file position if not working with a device }
  if not isdevice then
    { if we didn't modify the buffer, simply restore the BufPos and BufEnd  }
    { (the latter because it's now probably set to zero because nothing was }
    {  was read anymore)                                                    }
    if (reads = 0) then
      begin
        TextRec(t).BufPos:=oldbufpos;
        TextRec(t).BufEnd:=oldbufend;
      end
    { otherwise return to the old filepos and reset the buffer }
    else
      begin
        do_seek(TextRec(t).handle,oldfilepos);
        InOutRes:=0;
        FileFunc(TextRec(t).InOutFunc)(TextRec(t));
        TextRec(t).BufPos:=oldbufpos;
      end;
End;


Function SeekEof : Boolean;
Begin
  SeekEof:=SeekEof(Input);
End;


Function Eoln(var t:Text) : Boolean;
Begin
  If (InOutRes<>0) then
   exit(true);
  if (TextRec(t).mode<>fmInput) Then
   begin
     if TextRec(t).mode=fmOutPut then
      InOutRes:=104
     else
      InOutRes:=103;
     exit(true);
   end;
  If TextRec(t).BufPos>=TextRec(t).BufEnd Then
   begin
     FileFunc(TextRec(t).InOutFunc)(TextRec(t));
     If TextRec(t).BufPos>=TextRec(t).BufEnd Then
      exit(true);
   end;
  if CtrlZMarksEOF and (TextRec (T).BufPtr^[TextRec (T).BufPos] = #26) then
   exit (true);
  Eoln:=(TextRec(t).Bufptr^[TextRec(t).BufPos] in [#10,#13]);
End;


Function Eoln : Boolean;
Begin
  Eoln:=Eoln(Input);
End;


Function SeekEoln (Var t : Text) : Boolean;
Begin
  If (InOutRes<>0) then
   exit(true);
  if (TextRec(t).mode<>fmInput) Then
   begin
     if TextRec(t).mode=fmOutput then
      InOutRes:=104
     else
      InOutRes:=103;
     exit(true);
   end;
  repeat
    If TextRec(t).BufPos>=TextRec(t).BufEnd Then
     begin
       FileFunc(TextRec(t).InOutFunc)(TextRec(t));
       If TextRec(t).BufPos>=TextRec(t).BufEnd Then
        exit(true);
     end;
    case TextRec(t).Bufptr^[TextRec(t).BufPos] of
         #26: if CtrlZMarksEOF then
               exit (true);
     #10,#13 : exit(true);
      #9,' ' : ;
    else
     exit(false);
    end;
    inc(TextRec(t).BufPos);
  until false;
End;


Function SeekEoln : Boolean;
Begin
  SeekEoln:=SeekEoln(Input);
End;


Procedure SetTextBuf(Var F : Text; Var Buf; Size : SizeInt);
Begin
  TextRec(f).BufPtr:=@Buf;
  TextRec(f).BufSize:=Size;
  TextRec(f).BufPos:=0;
  TextRec(f).BufEnd:=0;
End;

Procedure SetTextLineEnding(Var f:Text; Ending:string);
Begin
  TextRec(F).LineEnd:=Ending;
End;

function GetTextCodePage(var T: Text): TSystemCodePage;
begin
{$if defined(FPC_HAS_CPSTRING) and defined(FPC_HAS_FEATURE_ANSISTRINGS)}
  GetTextCodePage:=TextRec(T).CodePage;
{$else}
  GetTextCodePage:=0;
{$endif}
end;


procedure SetTextCodePage(var T: Text; CodePage: TSystemCodePage);
begin
{$if defined(FPC_HAS_CPSTRING) and defined(FPC_HAS_FEATURE_ANSISTRINGS)}
  TextRec(T).CodePage:=TranslatePlaceholderCP(CodePage);
{$endif}
end;


Function fpc_get_input:PText;compilerproc;
begin
  fpc_get_input:=@Input;
end;


Function fpc_get_output:PText;compilerproc;
begin
  fpc_get_output:=@Output;
end;


Procedure fpc_textinit_iso(var t : Text;nr : DWord);compilerproc;
begin
{$ifdef FPC_HAS_FEATURE_COMMANDARGS}
  assign(t,paramstr(nr));
{$else FPC_HAS_FEATURE_COMMANDARGS}
  { primitive workaround for targets supporting no command line arguments,
    invent some file name, try to avoid complex procedures like concating strings which might
    pull-in bigger parts of the rtl }
  assign(t,chr((nr mod 16)+65));
{$endif FPC_HAS_FEATURE_COMMANDARGS}
end;


Procedure fpc_textclose_iso(var t : Text);compilerproc;
begin
  close(t);
end;


{*****************************************************************************
                               Write(Ln)
*****************************************************************************}

Procedure fpc_WriteBuffer(var f:Text;const b;len:SizeInt);
var
  p   : pchar;
  left,
  idx : SizeInt;
begin
  p:=pchar(@b);
  idx:=0;
  left:=TextRec(f).BufSize-TextRec(f).BufPos;
  while len>left do
   begin
     move(p[idx],TextRec(f).Bufptr^[TextRec(f).BufPos],left);
     dec(len,left);
     inc(idx,left);
     inc(TextRec(f).BufPos,left);
     FileFunc(TextRec(f).InOutFunc)(TextRec(f));
     left:=TextRec(f).BufSize-TextRec(f).BufPos;
   end;
  move(p[idx],TextRec(f).Bufptr^[TextRec(f).BufPos],len);
  inc(TextRec(f).BufPos,len);
end;


Procedure fpc_WriteBlanks(var f:Text;len:longint);
var
  left : longint;
begin
  left:=TextRec(f).BufSize-TextRec(f).BufPos;
  while len>left do
   begin
     FillChar(TextRec(f).Bufptr^[TextRec(f).BufPos],left,' ');
     dec(len,left);
     inc(TextRec(f).BufPos,left);
     FileFunc(TextRec(f).InOutFunc)(TextRec(f));
     left:=TextRec(f).BufSize-TextRec(f).BufPos;
   end;
  FillChar(TextRec(f).Bufptr^[TextRec(f).BufPos],len,' ');
  inc(TextRec(f).BufPos,len);
end;


Procedure fpc_Write_End(var f:Text); iocheck; compilerproc;
begin
  if TextRec(f).FlushFunc<>nil then
   FileFunc(TextRec(f).FlushFunc)(TextRec(f));
end;


Procedure fpc_Writeln_End(var f:Text); iocheck; compilerproc;
begin
  If InOutRes <> 0 then exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        { Write EOL }
        fpc_WriteBuffer(f,TextRec(f).LineEnd[1],length(TextRec(f).LineEnd));
        { Flush }
        if TextRec(f).FlushFunc<>nil then
          FileFunc(TextRec(f).FlushFunc)(TextRec(f));
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
end;


Procedure fpc_Write_Text_ShortStr(Len : Longint;var f : Text;const s : String); iocheck; [Public,Alias:'FPC_WRITE_TEXT_SHORTSTR']; compilerproc;
Begin
  If (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        If Len>Length(s) Then
          fpc_WriteBlanks(f,Len-Length(s));
        fpc_WriteBuffer(f,s[1],Length(s));
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
End;


Procedure fpc_Write_Text_ShortStr_Iso(Len : Longint;var f : Text;const s : String); iocheck; [Public,Alias:'FPC_WRITE_TEXT_SHORTSTR_ISO']; compilerproc;
Begin
  If (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        { default value? }
        If Len=-1 then
          Len:=length(s);

        If Len>Length(s) Then
          begin
            fpc_WriteBlanks(f,Len-Length(s));
            fpc_WriteBuffer(f,s[1],Length(s));
          end
        else
          fpc_WriteBuffer(f,s[1],Len);
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
End;


{ provide local access to write_str }
procedure Write_Str(Len : Longint;var f : Text;const s : String); iocheck; [external name 'FPC_WRITE_TEXT_SHORTSTR'];

{ provide local access to write_str_iso }
procedure Write_Str_Iso(Len : Longint;var f : Text;const s : String); iocheck; [external name 'FPC_WRITE_TEXT_SHORTSTR_ISO'];

Procedure fpc_Write_Text_Pchar_as_Array(Len : Longint;var f : Text;const s : array of char; zerobased: boolean = true); iocheck; compilerproc;
var
  ArrayLen : longint;
  p : pchar;
Begin
  If (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        p:=pchar(@s);
        if zerobased then
          begin
            { can't use StrLen, since that one could try to read past the end }
            { of the heap (JM)                                                }
            ArrayLen:=IndexByte(p^,high(s)+1,0);
            { IndexByte returns -1 if not found (JM) }
            if ArrayLen = -1 then
              ArrayLen := high(s)+1;
          end
        else
          ArrayLen := high(s)+1;
        If Len>ArrayLen Then
          fpc_WriteBlanks(f,Len-ArrayLen);
        fpc_WriteBuffer(f,p^,ArrayLen);
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
End;


Procedure fpc_Write_Text_Pchar_as_Array_Iso(Len : Longint;var f : Text;const s : array of char; zerobased: boolean = true); iocheck; compilerproc;
var
  ArrayLen : longint;
  p : pchar;
Begin
  If (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        p:=pchar(@s);
        if zerobased then
          begin
            { can't use StrLen, since that one could try to read past the end }
            { of the heap (JM)                                                }
            ArrayLen:=IndexByte(p^,high(s)+1,0);
            { IndexByte returns -1 if not found (JM) }
            if ArrayLen = -1 then
              ArrayLen := high(s)+1;
          end
        else
          ArrayLen := high(s)+1;

        { default value? }
        If Len=-1 then
          Len:=ArrayLen;

        If Len>ArrayLen Then
          begin
            fpc_WriteBlanks(f,Len-ArrayLen);
            fpc_WriteBuffer(f,p^,ArrayLen);
          end
        else
          fpc_WriteBuffer(f,p^,Len);
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
End;


Procedure fpc_Write_Text_PChar_As_Pointer(Len : Longint;var f : Text;p : PChar); iocheck; compilerproc;
var
  PCharLen : longint;
Begin
  If (p=nil) or (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        PCharLen:=StrLen(p);
        If Len>PCharLen Then
          fpc_WriteBlanks(f,Len-PCharLen);
        fpc_WriteBuffer(f,p^,PCharLen);
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
End;


Procedure fpc_Write_Text_AnsiStr (Len : Longint; Var f : Text; const S : RawByteString); iocheck; [Public,alias:'FPC_WRITE_TEXT_ANSISTR']; compilerproc;
{
 Writes a AnsiString to the Text file T
}
var
  SLen: longint;
  a: RawByteString;
begin
  If (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        SLen:=Length(s);
        If Len>SLen Then
          fpc_WriteBlanks(f,Len-SLen);
        if SLen > 0 then
          begin
            {$if defined(FPC_HAS_CPSTRING) and defined(FPC_HAS_FEATURE_ANSISTRINGS)}
            if TextRec(f).CodePage<>TranslatePlaceholderCP(StringCodePage(S)) then
              begin
                a:=fpc_AnsiStr_To_AnsiStr(S,TextRec(f).CodePage);
                fpc_WriteBuffer(f,PAnsiChar(a)^,Length(a));
              end
            else
            {$endif}
            fpc_WriteBuffer(f,PAnsiChar(s)^,SLen);
          end;
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
end;


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
Procedure fpc_Write_Text_UnicodeStr (Len : Longint; Var f : Text; const S : UnicodeString); iocheck; compilerproc;
{
 Writes a UnicodeString to the Text file T
}
var
  SLen: longint;
  a: RawByteString;
begin
  If (pointer(S)=nil) or (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        SLen:=Length(s);
        If Len>SLen Then
          fpc_WriteBlanks(f,Len-SLen);
        {$ifdef FPC_HAS_CPSTRING}
        WideStringManager.Unicode2AnsiMoveProc(PUnicodeChar(S),a,TextRec(f).CodePage,SLen);
        {$else}
        a:=s;
        {$endif FPC_HAS_CPSTRING}
        { length(a) can be > slen, e.g. after utf-16 -> utf-8 }
        fpc_WriteBuffer(f,PAnsiChar(a)^,Length(a));
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
end;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}


{$ifndef FPC_WIDESTRING_EQUAL_UNICODESTRING}
Procedure fpc_Write_Text_WideStr (Len : Longint; Var f : Text; const S : WideString); iocheck; compilerproc;
{
 Writes a WideString to the Text file T
}
var
  SLen: longint;
  a: RawByteString;
begin
  If (pointer(S)=nil) or (InOutRes<>0) then
   exit;
  case TextRec(f).mode of
    fmOutput { fmAppend gets changed to fmOutPut in do_open (JM) }:
      begin
        SLen:=Length(s);
        If Len>SLen Then
          fpc_WriteBlanks(f,Len-SLen);
        {$ifdef FPC_HAS_CPSTRING}
        widestringmanager.Wide2AnsiMoveProc(PWideChar(s), a, TextRec(f).CodePage, SLen);
        {$else}
        a:=s;
        {$endif}
        { length(a) can be > slen, e.g. after utf-16 -> utf-8 }
        fpc_WriteBuffer(f,PAnsiChar(a)^,Length(a));
      end;
    fmInput: InOutRes:=105
    else InOutRes:=103;
  end;
end;
{$endif FPC_WIDESTRING_EQUAL_UNICODESTRING}

Procedure fpc_Write_Text_SInt(Len : Longint;var t : Text;l : ValSInt); iocheck; compilerproc;
var
  s : String;
Begin
  If (InOutRes<>0) then
   exit;
  Str(l,s);
  Write_Str(Len,t,s);
End;


Procedure fpc_Write_Text_UInt(Len : Longint;var t : Text;l : ValUInt); iocheck; compilerproc;
var
  s : String;
Begin
  If (InOutRes<>0) then
   exit;
  Str(L,s);
  Write_Str(Len,t,s);
End;


Procedure fpc_Write_Text_SInt_Iso(Len : Longint;var t : Text;l : ValSInt); iocheck; compilerproc;
var
  s : String;
Begin
  If (InOutRes<>0) then
   exit;
  Str(l,s);
  { default value? }
  if len=-1 then
    len:=11
  else if len<length(s) then
    len:=length(s);
  Write_Str_Iso(Len,t,s);
End;


Procedure fpc_Write_Text_UInt_Iso(Len : Longint;var t : Text;l : ValUInt); iocheck; compilerproc;
var
  s : String;
Begin
  If (InOutRes<>0) then
   exit;
  Str(L,s);
  { default value? }
  if len=-1 then
    len:=11
  else if len<length(s) then
    len:=length(s);
  Write_Str_Iso(Len,t,s);
End;

{$ifndef CPU64}
procedure fpc_write_text_qword(len : longint;var t : text;q : qword); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(q,s);
  write_str(len,t,s);
end;


procedure fpc_write_text_int64(len : longint;var t : text;i : int64); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(i,s);
  write_str(len,t,s);
end;


procedure fpc_write_text_qword_iso(len : longint;var t : text;q : qword); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
    exit;
  str(q,s);
  { default value? }
  if len=-1 then
    len:=20
  else if len<length(s) then
    len:=length(s);
  write_str_iso(len,t,s);
end;


procedure fpc_write_text_int64_iso(len : longint;var t : text;i : int64); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(i,s);
  { default value? }
  if len=-1 then
    len:=20
  else if len<length(s) then
    len:=length(s);
  write_str_iso(len,t,s);
end;

{$endif CPU64}

{$if defined(CPU16) or defined(CPU8)}
procedure fpc_write_text_longword(len : longint;var t : text;q : longword); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(q,s);
  write_str(len,t,s);
end;


procedure fpc_write_text_longint(len : longint;var t : text;i : longint); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(i,s);
  write_str(len,t,s);
end;


procedure fpc_write_text_longword_iso(len : longint;var t : text;q : longword); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
    exit;
  str(q,s);
  { default value? }
  if len=-1 then
    len:=11
  else if len<length(s) then
    len:=length(s);
  write_str_iso(len,t,s);
end;


procedure fpc_write_text_longint_iso(len : longint;var t : text;i : longint); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(i,s);
  { default value? }
  if len=-1 then
    len:=11
  else if len<length(s) then
    len:=length(s);
  write_str_iso(len,t,s);
end;


procedure fpc_write_text_word(len : longint;var t : text;q : word); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(q,s);
  write_str(len,t,s);
end;


procedure fpc_write_text_smallint(len : longint;var t : text;i : smallint); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(i,s);
  write_str(len,t,s);
end;


procedure fpc_write_text_word_iso(len : longint;var t : text;q : word); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
    exit;
  str(q,s);
  { default value? }
  if len=-1 then
    len:=11
  else if len<length(s) then
    len:=length(s);
  write_str_iso(len,t,s);
end;


procedure fpc_write_text_smallint_iso(len : longint;var t : text;i : smallint); iocheck; compilerproc;
var
  s : string;
begin
  if (InOutRes<>0) then
   exit;
  str(i,s);
  { default value? }
  if len=-1 then
    len:=11
  else if len<length(s) then
    len:=length(s);
  write_str_iso(len,t,s);
end;
{$endif CPU16 or CPU8}

{$ifndef FPUNONE}
Procedure fpc_Write_Text_Float(rt,fixkomma,Len : Longint;var t : Text;r : ValReal); iocheck; compilerproc;
var
  s : String;
Begin
  If (InOutRes<>0) then
   exit;
  Str_real(Len,fixkomma,r,treal_type(rt),s);
  Write_Str(Len,t,s);
End;


Procedure fpc_Write_Text_Float_iso(rt,fixkomma,Len : Longint;var t : Text;r : ValReal); iocheck; compilerproc;
var
  s : String;
Begin
  If (InOutRes<>0) then
   exit;
  Str_real_iso(Len,fixkomma,r,treal_type(rt),s);
  Write_Str(Len,t,s);
End;
{$endif}

procedure fpc_write_text_enum(typinfo,ord2strindex:pointer;len:sizeint;var t:text;ordinal:longint); iocheck; compilerproc;

var
    s:string;

begin
  if textrec(t).mode<>fmoutput then
    begin
      if textrec(t).mode=fminput then
        inoutres:=105
      else
        inoutres:=103;
      exit;
    end;
  inoutres := fpc_shortstr_enum_intern(ordinal, len, typinfo, ord2strindex, s);
  if (inoutres <> 0) then
    exit;
  fpc_writeBuffer(t,s[1],length(s));
end;

{$ifdef FPC_HAS_STR_CURRENCY}
Procedure fpc_Write_Text_Currency(fixkomma,Len : Longint;var t : Text;c : Currency); iocheck; compilerproc;
var
  s : String;
Begin
  If (InOutRes<>0) then
   exit;
  str(c:Len:fixkomma,s);
  Write_Str(Len,t,s);
End;
{$endif FPC_HAS_STR_CURRENCY}

Procedure fpc_Write_Text_Boolean(Len : Longint;var t : Text;b : Boolean); iocheck; compilerproc;
Begin
  If (InOutRes<>0) then
   exit;
{ Can't use array[boolean] because b can be >0 ! }
  if b then
    Write_Str(Len,t,'TRUE')
  else
    Write_Str(Len,t,'FALSE');
End;


Procedure fpc_Write_Text_Boolean_Iso(Len : Longint;var t : Text;b : Boolean); iocheck; compilerproc;
Begin
  If (InOutRes<>0) then
   exit;
  { Can't use array[boolean] because b can be >0 ! }
  { default value? }
  If Len=-1 then
    Len:=5;
  if b then
    Write_Str_Iso(Len,t,'true')
  else
    Write_Str_Iso(Len,t,'false');
End;


Procedure fpc_Write_Text_Char(Len : Longint;var t : Text;c : Char); iocheck; compilerproc;
Begin
  If (InOutRes<>0) then
    exit;
  if (TextRec(t).mode<>fmOutput) Then
   begin
     if TextRec(t).mode=fmClosed then
      InOutRes:=103
     else
      InOutRes:=105;
     exit;
   end;
  If Len>1 Then
    fpc_WriteBlanks(t,Len-1);
  If TextRec(t).BufPos>=TextRec(t).BufSize Then
    FileFunc(TextRec(t).InOutFunc)(TextRec(t));
  TextRec(t).Bufptr^[TextRec(t).BufPos]:=c;
  Inc(TextRec(t).BufPos);
End;


Procedure fpc_Write_Text_Char_Iso(Len : Longint;var t : Text;c : Char); iocheck; compilerproc;
Begin
  If (InOutRes<>0) then
    exit;
  if (TextRec(t).mode<>fmOutput) Then
   begin
     if TextRec(t).mode=fmClosed then
      InOutRes:=103
     else
      InOutRes:=105;
     exit;
   end;
  { default value? }
  If Len=-1 then
    Len:=1;
  If Len>1 Then
    fpc_WriteBlanks(t,Len-1)
  else If Len<1 Then
    exit;
  If TextRec(t).BufPos>=TextRec(t).BufSize Then
    FileFunc(TextRec(t).InOutFunc)(TextRec(t));
  TextRec(t).Bufptr^[TextRec(t).BufPos]:=c;
  Inc(TextRec(t).BufPos);
End;


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
Procedure fpc_Write_Text_WideChar(Len : Longint;var t : Text;c : WideChar); iocheck; compilerproc;
var
  a: RawByteString;
Begin
  If (InOutRes<>0) then
    exit;
  if (TextRec(t).mode<>fmOutput) Then
   begin
     if TextRec(t).mode=fmClosed then
      InOutRes:=103
     else
      InOutRes:=105;
     exit;
   end;
  If Len>1 Then
    fpc_WriteBlanks(t,Len-1);
  If TextRec(t).BufPos>=TextRec(t).BufSize Then
    FileFunc(TextRec(t).InOutFunc)(TextRec(t));
  { a widechar can be translated into more than a single ansichar }
  {$ifdef FPC_HAS_CPSTRING}
  widestringmanager.Wide2AnsiMoveProc(@c,a,TextRec(t).CodePage,1);
  {$else}
  a:=c;
  {$endif}
  fpc_WriteBuffer(t,PAnsiChar(a)^,Length(a));
End;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}


{*****************************************************************************
                                Read(Ln)
*****************************************************************************}

Function NextChar(var f:Text;var s:string):Boolean;
begin
  NextChar:=false;
  if (TextRec(f).BufPos<TextRec(f).BufEnd) then
   if not (CtrlZMarksEOF) or (TextRec(f).Bufptr^[TextRec(f).BufPos]<>#26) then
    begin
     if length(s)<high(s) then
      begin
        inc(s[0]);
        s[length(s)]:=TextRec(f).BufPtr^[TextRec(f).BufPos];
      end;
     Inc(TextRec(f).BufPos);
     If TextRec(f).BufPos>=TextRec(f).BufEnd Then
      FileFunc(TextRec(f).InOutFunc)(TextRec(f));
     NextChar:=true;
   end;
end;


Function IgnoreSpaces(var f:Text):Boolean;
{
  Removes all leading spaces,tab,eols from the input buffer, returns true if
  the buffer is empty
}
var
  s : string;
begin
  s:='';
  IgnoreSpaces:=false;
  { Return false when already at EOF }
  if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
   exit;
(* Check performed separately to avoid accessing memory outside buffer *)
  if CtrlZMarksEOF and (TextRec(f).Bufptr^[TextRec(f).BufPos]=#26) then
   exit;
  while (TextRec(f).Bufptr^[TextRec(f).BufPos] <= ' ') do
   begin
     if not NextChar(f,s) then
      exit;
     { EOF? }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      break;
     if CtrlZMarksEOF and (TextRec(f).Bufptr^[TextRec(f).BufPos]=#26) then
      break;
   end;
  IgnoreSpaces:=true;
end;


procedure ReadNumeric(var f:Text;var s:string);
{
  Read numeric input, if buffer is empty then return True
}
begin
  repeat
    if not NextChar(f,s) then
      exit;
  until (length(s)=high(s)) or (TextRec(f).BufPtr^[TextRec(f).BufPos] <= ' ');
end;


function CheckRead(var f:Text):Boolean;
begin
  CheckRead:=False;
{ Check error and if file is open and load buf if empty }
  If (InOutRes<>0) then
    exit;
  if (TextRec(f).mode<>fmInput) Then
    begin
      case TextRec(f).mode of
        fmOutPut,fmAppend:
          InOutRes:=104;
        else
          InOutRes:=103;
      end;
      exit;
    end;
  if TextRec(f).BufPos>=TextRec(f).BufEnd Then
    FileFunc(TextRec(f).InOutFunc)(TextRec(f));
  CheckRead:=True;
end;


procedure ReadInteger(var f:Text;var s:string);
{
 Ignore leading blanks (incl. EOF) and return the first characters matching
 an integer in the format recognized by the Val procedure:
      [+-]?[0-9]+
   or [+-]?(0x|0X|x|X)[0-9A-Za-z]+
   or [+-]?&[0-7]+
   or [+-]?%[0-1]+
 A partial match may be returned, e.g.: '' or '+' or '0x'.
 Used by some fpc_Read_Text_*_Iso functions which implement the read()
 standard function in ISO mode.
}
var
  Base: Integer;
begin
    s := '';
    with TextRec(f) do begin
        if not CheckRead(f) then Exit;

        IgnoreSpaces(f);

        if BufPos >= BufEnd then Exit;
        if BufPtr^[BufPos] in ['+','-'] then
            NextChar(f,s);

        Base := 10;

        if BufPos >= BufEnd then Exit;
        if BufPtr^[BufPos] in ['$','x','X','%','&'] then
        begin
            case BufPtr^[BufPos] of
              '$','x','X': Base := 16;
	      '%': Base := 2;
              '&': Base := 8;
	    end;
            NextChar(f,s);
        end else if BufPtr^[BufPos] = '0' then
        begin
            NextChar(f,s);
            if BufPos >= BufEnd then Exit;
            if BufPtr^[BufPos] in ['x','X'] then
            begin
                Base := 16;
                NextChar(f,s);
            end;
        end;

        while (BufPos < BufEnd) and (Length(s) < High(s)) do
            if (((Base = 2) and (BufPtr^[BufPos] in ['0'..'1']))
	      or ((Base = 8) and (BufPtr^[BufPos] in ['0'..'7']))
              or ((Base = 10) and (BufPtr^[BufPos] in ['0'..'9']))
              or ((Base = 16) and (BufPtr^[BufPos] in ['0'..'9','a'..'f','A'..'F']))) then
                 NextChar(f,s)
	    else Exit;
   end;
end;


procedure ReadReal(var f:Text;var s:string);
{
 Ignore leading blanks (incl. EOF) and return the first characters matching
 a float number in the format recognized by the Val procedure:
      [+-]?([0-9]+)?\.[0-9]+([eE][+-]?[0-9]+)?
   or [+-]?[0-9]+\.([0-9]+)?([eE][+-]?[0-9]+)?
 A partial match may be returned, e.g.: '' or '+' or '.' or '1e' or even '+.'.
 Used by some fpc_Read_Text_*_Iso functions which implement the read()
 standard function in ISO mode.
}
var digit: Boolean;
begin
    s := '';
    with TextRec(f) do begin
        if not CheckRead(f) then Exit;

        IgnoreSpaces(f);

        if BufPos >= BufEnd then Exit;
        if BufPtr^[BufPos] in ['+','-'] then
            NextChar(f,s);

        digit := false;
        if BufPos >= BufEnd then Exit;
	if BufPtr^[BufPos] in ['0'..'9'] then
        begin
            digit := true;
            repeat
                NextChar(f,s);
                if (BufPos >= BufEnd) or (Length(s) >= High(s)) then Exit;
            until not (BufPtr^[BufPos] in ['0'..'9']);
        end;

        if BufPtr^[BufPos] = '.' then
        begin
            NextChar(f,s);

            if (BufPos >= BufEnd) or (Length(s) >= High(s)) then Exit;
	    if BufPtr^[BufPos] in ['0'..'9'] then
            begin
                digit := true;
                repeat
                    NextChar(f,s);
                    if (BufPos >= BufEnd) or (Length(s) >= High(s)) then Exit;
                until not (BufPtr^[BufPos] in ['0'..'9']);
            end;
        end;

        {at least one digit is required on the left of the exponent}
        if digit and (BufPtr^[BufPos] in ['e','E']) then
        begin
            NextChar(f,s);

            if (BufPos >= BufEnd) or (Length(s) >= High(s)) then Exit;
            if BufPtr^[BufPos] in ['+','-'] then
                NextChar(f,s);

	    while (BufPos < BufEnd) and (Length(s) < High(s)) do
                if BufPtr^[BufPos] in ['0'..'9'] then
                    NextChar(f,s)
                else break;
        end;
    end;
end;


Procedure fpc_Read_End(var f:Text);[Public,Alias:'FPC_READ_END']; iocheck; compilerproc;
begin
  if TextRec(f).FlushFunc<>nil then
   FileFunc(TextRec(f).FlushFunc)(TextRec(f));
end;


Procedure fpc_ReadLn_End(var f : Text);[Public,Alias:'FPC_READLN_END']; iocheck; compilerproc;
var prev: char;
Begin
  If not CheckRead(f) then
    exit;
  if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
    { Flush if set }
    begin
      if (TextRec(f).FlushFunc<>nil) then
        FileFunc(TextRec(f).FlushFunc)(TextRec(f));
      exit;
    end;
  if CtrlZMarksEOF and (TextRec (F).BufPtr^ [TextRec (F).BufPos] = #26) then
   Exit;
  repeat
    prev := TextRec(f).BufPtr^[TextRec(f).BufPos];
    inc(TextRec(f).BufPos);
{ no system uses #10#13 as line seperator (#10 = *nix, #13 = Mac, }
{ #13#10 = Dos), so if we've got #10, we can safely exit          }
    if prev = #10 then
      exit;
    {$ifdef MACOS}
    if prev = #13 then
      {StdInput on macos never have dos line ending, so this is safe.}
      if TextRec(f).Handle = StdInputHandle then
        exit;
    {$endif MACOS}
    if TextRec(f).BufPos>=TextRec(f).BufEnd Then
      begin
        FileFunc(TextRec(f).InOutFunc)(TextRec(f));
        if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
          { Flush if set }
          begin
           if (TextRec(f).FlushFunc<>nil) then
             FileFunc(TextRec(f).FlushFunc)(TextRec(f));
           exit;
         end;
      end;
   if CtrlZMarksEOF and (TextRec (F).BufPtr^ [TextRec (F).BufPos] = #26) then
    Exit;
   if (prev=#13) then
     { is there also a #10 after it? }
     begin
       if (TextRec(f).BufPtr^[TextRec(f).BufPos]=#10) then
         { yes, skip that one as well }
         inc(TextRec(f).BufPos);
       exit;
     end;
  until false;
End;


Procedure fpc_ReadLn_End_Iso(var f : Text);[Public,Alias:'FPC_READLN_END_ISO']; iocheck; compilerproc;
var prev: char;
Begin
  If not CheckRead(f) then
    exit;
  if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
    { Flush if set }
    begin
      if (TextRec(f).FlushFunc<>nil) then
        FileFunc(TextRec(f).FlushFunc)(TextRec(f));
      exit;
    end;
  if TextRec (F).BufPtr^ [TextRec (F).BufPos] = #26 then
    begin
      inc(TextRec(f).BufPos);
      Exit;
    end;
  repeat
    prev := TextRec(f).BufPtr^[TextRec(f).BufPos];
    inc(TextRec(f).BufPos);
{ no system uses #10#13 as line seperator (#10 = *nix, #13 = Mac, }
{ #13#10 = Dos), so if we've got #10, we can safely exit          }
    if prev = #10 then
      exit;
    {$ifdef MACOS}
    if prev = #13 then
      {StdInput on macos never have dos line ending, so this is safe.}
      if TextRec(f).Handle = StdInputHandle then
        exit;
    {$endif MACOS}
    if TextRec(f).BufPos>=TextRec(f).BufEnd Then
      begin
        FileFunc(TextRec(f).InOutFunc)(TextRec(f));
        if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
          { Flush if set }
          begin
           if (TextRec(f).FlushFunc<>nil) then
             FileFunc(TextRec(f).FlushFunc)(TextRec(f));
           exit;
         end;
      end;
   if TextRec (F).BufPtr^ [TextRec (F).BufPos] = #26 then
     begin
       inc(TextRec(f).BufPos);
       Exit;
     end;
   if (prev=#13) then
     { is there also a #10 after it? }
     begin
       if (TextRec(f).BufPtr^[TextRec(f).BufPos]=#10) then
         { yes, skip that one as well }
         inc(TextRec(f).BufPos);
       exit;
     end;
  until false;
End;


Function ReadPCharLen(var f:Text;s:pchar;maxlen:longint):longint;
var
  sPos,len : Longint;
  p,startp,maxp : pchar;
  end_of_string:boolean;
Begin
  ReadPCharLen:=0;
  If not CheckRead(f) then
    exit;
{ Read maximal until Maxlen is reached }
  sPos:=0;
  end_of_string:=false;
  repeat
    If TextRec(f).BufPos>=TextRec(f).BufEnd Then
     begin
       FileFunc(TextRec(f).InOutFunc)(TextRec(f));
       If TextRec(f).BufPos>=TextRec(f).BufEnd Then
         break;
     end;
    p:=@TextRec(f).Bufptr^[TextRec(f).BufPos];
    if SPos+TextRec(f).BufEnd-TextRec(f).BufPos>MaxLen then
     maxp:=@TextRec(f).BufPtr^[TextRec(f).BufPos+MaxLen-SPos]
    else
     maxp:=@TextRec(f).Bufptr^[TextRec(f).BufEnd];
    startp:=p;
  { find stop character }
    while p<maxp do
      begin
        { Optimization: Do a quick check for a control character first }
        if (p^<' ') then
          begin
            if (p^ in [#10,#13]) or
               (ctrlZmarkseof and (p^=#26)) then
              begin
                end_of_string:=true;
                break;
              end;
          end;
        inc(p);
      end;
  { calculate read bytes }
    len:=p-startp;
    inc(TextRec(f).BufPos,Len);
    Move(startp^,s[sPos],Len);
    inc(sPos,Len);
  until (spos=MaxLen) or end_of_string;
  ReadPCharLen:=spos;
End;


Procedure fpc_Read_Text_ShortStr(var f : Text;out s : String); iocheck; compilerproc;
Begin
  s[0]:=chr(ReadPCharLen(f,pchar(@s[1]),high(s)));
End;


Procedure fpc_Read_Text_PChar_As_Pointer(var f : Text; const s : PChar); iocheck; compilerproc;
Begin
  pchar(s+ReadPCharLen(f,s,$7fffffff))^:=#0;
End;


Procedure fpc_Read_Text_PChar_As_Array(var f : Text;out s : array of char; zerobased: boolean = false); iocheck; compilerproc;
var
  len: longint;
Begin
  len := ReadPCharLen(f,pchar(@s),high(s)+1);
  if zerobased and
     (len > high(s)) then
    len := high(s);
  if (len <= high(s)) then
    s[len] := #0;
End;


{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
Procedure fpc_Read_Text_AnsiStr(var f : Text;out s : RawByteString{$ifdef FPC_HAS_CPSTRING};cp : TSystemCodePage{$endif FPC_HAS_CPSTRING}); [public, alias: 'FPC_READ_TEXT_ANSISTR']; iocheck; compilerproc;
var
  slen,len : SizeInt;
Begin
  slen:=0;
  Repeat
    // SetLength will reallocate the length.
    SetLength(s,slen+255);
    len:=ReadPCharLen(f,pchar(Pointer(s)+slen),255);
    inc(slen,len);
  Until len<255;
  // Set actual length
  SetLength(s,Slen);
  {$ifdef FPC_HAS_CPSTRING}
  SetCodePage(s,TextRec(f).CodePage,false);
  if cp<>TextRec(f).CodePage then
    s:=fpc_AnsiStr_To_AnsiStr(s,cp);
  {$endif FPC_HAS_CPSTRING}
End;

Procedure fpc_Read_Text_AnsiStr_Intern(var f : Text;out s : RawByteString{$ifdef FPC_HAS_CPSTRING};cp : TSystemCodePage{$endif FPC_HAS_CPSTRING}); [external name 'FPC_READ_TEXT_ANSISTR'];
{$endif FPC_HAS_FEATURE_ANSISTRINGS}


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
Procedure fpc_Read_Text_UnicodeStr(var f : Text;out us : UnicodeString); iocheck; compilerproc;
var
  s: RawByteString;
Begin
  // all standard input is assumed to be ansi-encoded
  fpc_Read_Text_AnsiStr_Intern(f,s{$ifdef FPC_HAS_CPSTRING},DefaultSystemCodePage{$endif FPC_HAS_CPSTRING});
  // Convert to unicodestring
  {$ifdef FPC_HAS_CPSTRING}
  widestringmanager.Ansi2UnicodeMoveProc(PAnsiChar(s),StringCodePage(s),us,Length(s));
  {$else}
  us:=s;
  {$endif}
End;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}

{$ifndef FPC_WIDESTRING_EQUAL_UNICODESTRING}
Procedure fpc_Read_Text_WideStr(var f : Text;out ws : WideString); iocheck; compilerproc;
var
  s: RawByteString;
Begin
  // all standard input is assumed to be ansi-encoded
  fpc_Read_Text_AnsiStr_Intern(f,s{$ifdef FPC_HAS_CPSTRING},DefaultSystemCodePage{$endif FPC_HAS_CPSTRING});
  // Convert to widestring
  {$ifdef FPC_HAS_CPSTRING}
  widestringmanager.Ansi2WideMoveProc(PAnsiChar(s),StringCodePage(s),ws,Length(s));
  {$else}
  ws:=s;
  {$endif}
End;
{$endif FPC_WIDESTRING_EQUAL_UNICODESTRING}

procedure fpc_Read_Text_Char(var f : Text; out c: char); [public, alias: 'FPC_READ_TEXT_CHAR']; iocheck; compilerproc;
Begin
  c:=#0;
  If not CheckRead(f) then
    exit;
  If TextRec(f).BufPos>=TextRec(f).BufEnd Then
    begin
      c := #26;
      exit;
    end;
  c:=TextRec(f).Bufptr^[TextRec(f).BufPos];
  inc(TextRec(f).BufPos);
end;

procedure fpc_Read_Text_Char_intern(var f : Text; out c: char); iocheck; [external name 'FPC_READ_TEXT_CHAR'];


function fpc_GetBuf_Text(var f : Text) : pchar; iocheck; compilerproc;
Begin
  Result:=@TextRec(f).Bufptr^[TextRec(f).BufEnd];
  If not CheckRead(f) then
    exit;
  If TextRec(f).BufPos>=TextRec(f).BufEnd Then
    exit;
  Result:=@TextRec(f).Bufptr^[TextRec(f).BufPos];
end;


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
procedure fpc_Read_Text_WideChar(var f : Text; out wc: widechar); iocheck;compilerproc;
var
  ws: widestring;
  i: longint;
  { maximum code point length is 6 characters (with UTF-8) }
  str: array[0..5] of char;
Begin
  fillchar(str[0],sizeof(str),0);
  for i:=low(str) to high(str) do
    begin
      fpc_Read_Text_Char_intern(f,str[i]);
      case widestringmanager.CodePointLengthProc(@str[0],i+1) of
        -1: { possibly incomplete code point, try with an extra character }
           ;
        0: { null character }
          begin
            wc:=#0;
            exit;
          end;
        else
          begin
            { valid code point -> convert to widestring}
            {$ifdef FPC_HAS_CPSTRING}
            widestringmanager.Ansi2WideMoveProc(@str[0],TextRec(f).CodePage,ws,i+1);
            {$else}
            widestringmanager.Ansi2WideMoveProc(@str[0],DefaultSystemCodePage,ws,i+1);
            {$endif}
            { has to be exactly one widechar }
            if length(ws)=1 then
              begin
                wc:=ws[1];
                exit
              end
            else
              break;
          end;
      end;
    end;
  { invalid widechar input }
  inoutres:=106;
end;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}


procedure fpc_Read_Text_Char_Iso(var f : Text; out c: char); iocheck;compilerproc;
Begin
  c:=' ';
  If not CheckRead(f) then
    exit;
  If TextRec(f).BufPos>=TextRec(f).BufEnd Then
    begin
      c:=' ';
      exit;
    end;
  c:=TextRec(f).Bufptr^[TextRec(f).BufPos];
  inc(TextRec(f).BufPos);
  if c=#13 then
    begin
      c:=' ';
      If not CheckRead(f) or
        (TextRec(f).BufPos>=TextRec(f).BufEnd) then
        exit;
      If TextRec(f).Bufptr^[TextRec(f).BufPos]=#10 then
        inc(TextRec(f).BufPos);

      { ignore #26 following a new line }
      If not CheckRead(f) or
        (TextRec(f).BufPos>=TextRec(f).BufEnd) then
        exit;
      If TextRec(f).Bufptr^[TextRec(f).BufPos]=#26 then
        inc(TextRec(f).BufPos);
    end
  else if c=#10 then
    begin
      c:=' ';
      { ignore #26 following a new line }
      If not CheckRead(f) or
        (TextRec(f).BufPos>=TextRec(f).BufEnd) then
        exit;
      If TextRec(f).Bufptr^[TextRec(f).BufPos]=#26 then
        inc(TextRec(f).BufPos);
      end
  else if c=#26 then
    c:=' ';
end;


Procedure fpc_Read_Text_SInt(var f : Text; out l : ValSInt); iocheck; compilerproc;
var
  hs   : String;
  code : ValSInt;
Begin
  l:=0;
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     if CtrlZMarksEOF and (TextRec(f).Bufptr^[TextRec(f).BufPos]=#26) then
      exit;
     ReadNumeric(f,hs);
   end;
   if (hs = '') then
    L := 0
   else
    begin
     Val(hs,l,code);
     if Code <> 0 then
      InOutRes:=106;
    end;
End;


Procedure fpc_Read_Text_SInt_Iso(var f : Text; out l : ValSInt); iocheck; compilerproc;
var
  hs   : String;
  code : ValSInt;
Begin
    ReadInteger(f,hs);

    Val(hs,l,code);
    if Code <> 0 then
        InOutRes:=106;
End;


Procedure fpc_Read_Text_UInt(var f : Text; out u : ValUInt);  iocheck; compilerproc;
var
  hs   : String;
  code : ValSInt;
Begin
  u:=0;
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     ReadNumeric(f,hs);
   end;
   if (hs = '') then
    u := 0
   else
    begin
      val(hs,u,code);
      If code<>0 Then
        InOutRes:=106;
    end;
End;

Procedure fpc_Read_Text_UInt_Iso(var f : Text; out u : ValUInt);  iocheck; compilerproc;
var
  hs   : String;
  code : ValSInt;
Begin
   ReadInteger(f,hs);
   Val(hs,u,code);
   If code<>0 Then
       InOutRes:=106;
End;


{$ifndef FPUNONE}
procedure fpc_Read_Text_Float(var f : Text; out v : ValReal); iocheck; compilerproc;
var
  hs : string;
  code : Word;
begin
  v:=0.0;
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     ReadNumeric(f,hs);
   end;
  val(hs,v,code);
  If code<>0 Then
   InOutRes:=106;
end;


procedure fpc_Read_Text_Float_Iso(var f : Text; out v : ValReal); iocheck; compilerproc;
var
  hs : string;
  code : Word;
begin
  ReadReal(f,hs);
  Val(hs,v,code);
  If code<>0 Then
    InOutRes:=106;
end;
{$endif}

procedure fpc_read_text_enum(str2ordindex:pointer;var t:text;out ordinal:longint); iocheck;compilerproc;

var s:string;
    code:valsint;

begin
  if not checkread(t) then
    exit;
  s:='';
  if ignorespaces(t) then
    begin
      { When spaces were found and we are now at EOF, then we return 0 }
      if (TextRec(t).BufPos>=TextRec(t).BufEnd) then
        exit;
      ReadNumeric(t,s);
    end;
  ordinal:=fpc_val_enum_shortstr(str2ordindex,s,code);
  if code<>0 then
   InOutRes:=106;
end;

procedure fpc_Read_Text_Currency(var f : Text; out v : Currency); iocheck; compilerproc;
var
  hs : string;
  code : ValSInt;
begin
{$ifdef FPUNONE}
  v:=0;
{$else}
  v:=0.0;
{$endif}
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     ReadNumeric(f,hs);
   end;
  val(hs,v,code);
  If code<>0 Then
   InOutRes:=106;
end;


procedure fpc_Read_Text_Currency_Iso(var f : Text; out v : Currency); iocheck; compilerproc;
var
  hs : string;
  code : ValSInt;
begin
  ReadReal(f,hs);
  Val(hs,v,code);
  If code<>0 Then
   InOutRes:=106;
end;


{$ifndef cpu64}

procedure fpc_Read_Text_QWord(var f : text; out q : qword); iocheck; compilerproc;
var
  hs   : String;
  code : longint;
Begin
  q:=0;
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     ReadNumeric(f,hs);
   end;
  val(hs,q,code);
  If code<>0 Then
   InOutRes:=106;
End;

procedure fpc_Read_Text_QWord_Iso(var f : text; out q : qword); iocheck; compilerproc;
var
  hs   : String;
  code : longint;
Begin
   ReadInteger(f,hs);
   Val(hs,q,code);
   If code<>0 Then
       InOutRes:=106;
End;

procedure fpc_Read_Text_Int64(var f : text; out i : int64); iocheck; compilerproc;
var
  hs   : String;
  code : Longint;
Begin
  i:=0;
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     ReadNumeric(f,hs);
   end;
  Val(hs,i,code);
  If code<>0 Then
   InOutRes:=106;
End;

procedure fpc_Read_Text_Int64_Iso(var f : text; out i : int64); iocheck; compilerproc;
var
  hs   : String;
  code : Longint;
Begin
    ReadInteger(f,hs);
    Val(hs,i,code);
    If code<>0 Then
       InOutRes:=106;
End;


{$endif CPU64}

{$if defined(CPU16) or defined(CPU8)}
procedure fpc_Read_Text_LongWord(var f : text; out q : longword); iocheck; compilerproc;
var
  hs   : String;
  code : longint;
Begin
  q:=0;
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     ReadNumeric(f,hs);
   end;
  val(hs,q,code);
  If code<>0 Then
   InOutRes:=106;
End;

procedure fpc_Read_Text_LongInt(var f : text; out i : longint); iocheck; compilerproc;
var
  hs   : String;
  code : Longint;
Begin
  i:=0;
  If not CheckRead(f) then
    exit;
  hs:='';
  if IgnoreSpaces(f) then
   begin
     { When spaces were found and we are now at EOF,
       then we return 0 }
     if (TextRec(f).BufPos>=TextRec(f).BufEnd) then
      exit;
     ReadNumeric(f,hs);
   end;
  Val(hs,i,code);
  If code<>0 Then
   InOutRes:=106;
End;
{$endif CPU16 or CPU8}



{*****************************************************************************
                              WriteStr/ReadStr
*****************************************************************************}

const
  { pointer to target string }
  StrPtrIndex = 1;
  { temporary destination for writerstr, because the original value of the
    destination may be used in the writestr expression }
  TempWriteStrDestIndex = 9;
  ShortStrLenIndex = 17;
  { how many bytes of the string have been processed already (used for readstr) }
  BytesReadIndex = 17;

procedure WriteStrShort(var t: textrec);
var
  str: pshortstring;
  newbytes,
  oldlen: longint;
begin
  if (t.bufpos=0) then
    exit;
  str:=pshortstring(ppointer(@t.userdata[TempWriteStrDestIndex])^);
  newbytes:=t.BufPos;
  oldlen:=length(str^);
  if (oldlen+t.bufpos > t.userdata[ShortStrLenIndex]) then
    begin
      newbytes:=t.userdata[ShortStrLenIndex]-oldlen;
{$ifdef writestr_iolencheck}
      // GPC only gives an io error if {$no-truncate-strings} is active
      // FPC does not have this setting (it never gives errors when a
      // a string expression is truncated)

      { "disk full" }
      inoutres:=101;
{$endif}
    end;
  setlength(str^,length(str^)+newbytes);
  move(t.bufptr^,str^[oldlen+1],newbytes);
  t.bufpos:=0;
end;


procedure WriteStrShortFlush(var t: textrec);
begin
  { move written data from internal buffer to temporary string (don't move
    directly from buffer to final string, because the temporary string may
    already contain data in case the textbuf was smaller than the string
    length) }
  WriteStrShort(t);
  { move written data to original string }
  move(PPointer(@t.userdata[TempWriteStrDestIndex])^^,
       PPointer(@t.userdata[StrPtrIndex])^^,
       t.userdata[ShortStrLenIndex]+1);
  { free temporary buffer }
  freemem(PPointer(@t.userdata[TempWriteStrDestIndex])^);
end;



{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
procedure WriteStrAnsi(var t: textrec);
var
  str: pansistring;
  oldlen: longint;
begin
  if (t.bufpos=0) then
    exit;
  str:=pansistring(@t.userdata[TempWriteStrDestIndex]);
  oldlen:=length(str^);
  setlength(str^,oldlen+t.bufpos);
  move(t.bufptr^,str^[oldlen+1],t.bufpos);
  t.bufpos:=0;
end;


procedure WriteStrAnsiFlush(var t: textrec);
begin
  { see comment in WriteStrShortFlush }
  WriteStrAnsi(t);
  pansistring(ppointer(@t.userdata[StrPtrIndex])^)^:=
    pansistring(@t.userdata[TempWriteStrDestIndex])^;
  { free memory/finalize temp }
  pansistring(@t.userdata[TempWriteStrDestIndex])^:='';
end;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
function UTF8CodePointLength(firstbyte: byte): SizeInt;
var
  firstzerobit: SizeInt;
begin
  result:=1;
  { bsr searches for the leftmost 1 bit. We are interested in the
    leftmost 0 bit, so first invert the value
  }
  firstzerobit:=BsrByte(not(firstbyte));
  { if there is no zero bit or the first zero bit is the rightmost bit
    (bit 0), this is an invalid UTF-8 byte ($ff cannot appear in an
    UTF-8-encoded string, and in the worst case bit 1 has to be zero)
  }
  if (firstzerobit=0) or (firstzerobit=255)  then
    exit;
  { the number of bytes belonging to this code point is
    7-(pos first 0-bit).
  }
  result:=7-firstzerobit;
end;


function EndOfLastCompleteUTF8CodePoint(var t: textrec): SizeInt;
var
  i, lenfound, codepointlen: SizeInt;
  b: byte;
begin
  lenfound:=0;
  for i:=t.bufpos-1 downto 0 do
    begin
      b:=byte(t.bufptr^[i]);
      if b<=127 then
        begin
          if lenfound = 0 then
            { valid simple code point }
            result:=i+1
          else
            { valid simple code point followed by a bunch of invalid data ->
              handle everything since it can't become valid by adding more
              bytes }
            result:=t.bufpos;
          exit;
        end;
      { start of a complex character }
      if (b and %11000000)<>0 then
        begin
          codepointlen:=UTF8CodePointLength(b);
          { we did not yet get all bytes of the last code point -> handle
            everything until the start of this character }
          if codepointlen>lenfound+1 then
            if i<>0 then
              result:=i
            { the buffer is too small to contain the entire utf-8 code point
              -> nothing else to do but handle the entire buffer (and end up
              with an invalid character) -- since writestr uses the default
              buffer size of 32 bytes, this can only happen for invalid utf-8
              encodings }
            else
              result:=t.bufpos
          { the last code point is invalid -> handle everything since it can't
            become valid by adding more bytes; in case it's complete, we also
            handle everything, of course}
          else
            result:=t.bufpos;
          exit;
        end;
      inc(lenfound);
    end;
  { all invalid data, or the buffer is too small to be able to deal with the
    complete utf8char -> nothing else to do but to handle the entire buffer }
  result:=t.bufpos;
end;


procedure WriteStrUnicodeIntern(var t: textrec; flush: boolean);
var
  temp: unicodestring;
  str: punicodestring;
  validend: SizeInt;
begin
  if (t.bufpos=0) then
    exit;
  str:=punicodestring(@t.userdata[TempWriteStrDestIndex]);
  if not flush then
    validend:=EndOfLastCompleteUTF8CodePoint(t)
  else
    validend:=t.bufpos;
  widestringmanager.Ansi2UnicodeMoveProc(@t.bufptr^[0],CP_UTF8,temp,validend);
  str^:=str^+temp;
  dec(t.bufpos,validend);
  { move remainder to the start }
  if t.bufpos<>0 then
    move(t.bufptr^[validend],t.bufptr^[0],t.bufpos);
end;


procedure WriteStrUnicode(var t: textrec);
begin
  WriteStrUnicodeIntern(t,false);
end;


procedure WriteStrUnicodeFlush(var t: textrec);
begin
  { see comment in WriteStrShortFlush }
  WriteStrUnicodeIntern(t,true);
  punicodestring(ppointer(@t.userdata[StrPtrIndex])^)^:=
    punicodestring(@t.userdata[TempWriteStrDestIndex])^;
  { free memory/finalize temp }
  punicodestring(@t.userdata[TempWriteStrDestIndex])^:='';
end;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}

{$ifndef FPC_WIDESTRING_EQUAL_UNICODESTRING}
procedure WriteStrWideIntern(var t: textrec; flush: boolean);
var
  temp: unicodestring;
  str: pwidestring;
  validend: SizeInt;
begin
  if (t.bufpos=0) then
    exit;
  str:=pwidestring(@t.userdata[TempWriteStrDestIndex]);
  if not flush then
    validend:=EndOfLastCompleteUTF8CodePoint(t)
  else
    validend:=t.bufpos;
  widestringmanager.Ansi2UnicodeMoveProc(@t.bufptr^[0],CP_UTF8,temp,validend);
  str^:=str^+temp;
  dec(t.bufpos,validend);
  { move remainder to the start }
  if t.bufpos<>0 then
    move(t.bufptr^[validend],t.bufptr^[0],t.bufpos);
end;


procedure WriteStrWide(var t: textrec);
begin
  WriteStrUnicodeIntern(t,false);
end;


procedure WriteStrWideFlush(var t: textrec);
begin
  { see comment in WriteStrShortFlush }
  WriteStrWideIntern(t,true);
  pwidestring(ppointer(@t.userdata[StrPtrIndex])^)^:=
    pwidestring(@t.userdata[TempWriteStrDestIndex])^;
  { free memory/finalize temp }
  finalize(pwidestring(@t.userdata[TempWriteStrDestIndex])^);
end;
{$endif FPC_WIDESTRING_EQUAL_UNICODESTRING}

procedure SetupWriteStrCommon(out t: textrec; cp: TSystemCodePage);
begin
  // initialise
  Assign(text(t),'');
  t.mode:=fmOutput;
  t.OpenFunc:=nil;
  t.CloseFunc:=nil;
{$if defined(FPC_HAS_CPSTRING) and defined(FPC_HAS_FEATURE_ANSISTRINGS)}
  t.CodePage:=TranslatePlaceholderCP(cp);
{$endif}
end;


procedure fpc_SetupWriteStr_Shortstr(var ReadWriteStrText: text; var s: shortstring); compilerproc;
begin
  SetupWriteStrCommon(TextRec(ReadWriteStrText),DefaultSystemCodePage);
  PPointer(@TextRec(ReadWriteStrText).userdata[StrPtrIndex])^:=@s;

  { temporary destination (see comments for TempWriteStrDestIndex) }
  getmem(PPointer(@TextRec(ReadWriteStrText).userdata[TempWriteStrDestIndex])^,high(s)+1);
  setlength(pshortstring(ppointer(@TextRec(ReadWriteStrText).userdata[TempWriteStrDestIndex])^)^,0);

  TextRec(ReadWriteStrText).userdata[ShortStrLenIndex]:=high(s);
  TextRec(ReadWriteStrText).InOutFunc:=@WriteStrShort;
  TextRec(ReadWriteStrText).FlushFunc:=@WriteStrShortFlush;
end;


{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
procedure fpc_SetupWriteStr_Ansistr(var ReadWriteStrText: text; var s: ansistring; cp: TSystemCodePage); compilerproc;
begin
  { destination rawbytestring -> use CP_ACP }
  if cp=CP_NONE then
    cp:=CP_ACP;
  SetupWriteStrCommon(TextRec(ReadWriteStrText),cp);
  PPointer(@TextRec(ReadWriteStrText).userdata[StrPtrIndex])^:=@s;

  { temp destination ansistring, nil = empty string }
  PPointer(@TextRec(ReadWriteStrText).userdata[TempWriteStrDestIndex])^:=nil;

  TextRec(ReadWriteStrText).InOutFunc:=@WriteStrAnsi;
  TextRec(ReadWriteStrText).FlushFunc:=@WriteStrAnsiFlush;
end;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
procedure fpc_SetupWriteStr_Unicodestr(var ReadWriteStrText: text; var s: unicodestring); compilerproc;
begin
  SetupWriteStrCommon(TextRec(ReadWriteStrText),CP_UTF8);
  PPointer(@TextRec(ReadWriteStrText).userdata[StrPtrIndex])^:=@s;

  { temp destination unicodestring, nil = empty string }
  PPointer(@TextRec(ReadWriteStrText).userdata[TempWriteStrDestIndex])^:=nil;

  TextRec(ReadWriteStrText).InOutFunc:=@WriteStrUnicode;
  TextRec(ReadWriteStrText).FlushFunc:=@WriteStrUnicodeFlush;
end;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}


{$ifndef FPC_WIDESTRING_EQUAL_UNICODESTRING}
procedure fpc_SetupWriteStr_Widestr(var ReadWriteStrText: text; var s: widestring); compilerproc;
begin
  SetupWriteStrCommon(TextRec(ReadWriteStrText),CP_UTF8);
  PPointer(@TextRec(ReadWriteStrText).userdata[StrPtrIndex])^:=@s;

  { temp destination widestring }
  PWideString(@TextRec(ReadWriteStrText).userdata[TempWriteStrDestIndex])^:='';

  TextRec(ReadWriteStrText).InOutFunc:=@WriteStrWide;
  TextRec(ReadWriteStrText).FlushFunc:=@WriteStrWideFlush;
end;
{$endif FPC_WIDESTRING_EQUAL_UNICODESTRING}


{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
procedure ReadAnsiStrFinal(var t: textrec);
begin
  { finalise the temp ansistring }
  PAnsiString(@t.userdata[StrPtrIndex])^ := '';
end;
{$endif FPC_HAS_FEATURE_ANSISTRINGS}


procedure ReadStrCommon(var t: textrec; strdata: pchar; len: sizeint);
var
  newbytes: sizeint;
begin
  newbytes := len - PSizeInt(@t.userdata[BytesReadIndex])^;
  if (t.BufSize <= newbytes) then
    newbytes := t.BufSize;
  if (newbytes > 0) then
    begin
      move(strdata[PSizeInt(@t.userdata[BytesReadIndex])^],t.BufPtr^,newbytes);
      inc(PSizeInt(@t.userdata[BytesReadIndex])^,newbytes);
    end;
  t.BufEnd:=newbytes;
  t.BufPos:=0;
end;


procedure ReadStrAnsi(var t: textrec);
var
  str: pansistring;
begin
  str:=pansistring(@t.userdata[StrPtrIndex]);
  ReadStrCommon(t,@str^[1],length(str^));
end;


procedure SetupReadStrCommon(out t: textrec; cp: TSystemCodePage);
begin
  // initialise
  Assign(text(t),'');
  t.mode:=fmInput;
  t.OpenFunc:=nil;
  t.CloseFunc:=nil;
{$if defined(FPC_HAS_CPSTRING) and defined(FPC_HAS_FEATURE_ANSISTRINGS)}
  t.CodePage:=TranslatePlaceholderCP(cp);
  {$endif}
  PSizeInt(@t.userdata[BytesReadIndex])^:=0;
end;


{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
procedure fpc_SetupReadStr_Ansistr(var ReadWriteStrText: text; const s: ansistring); [public, alias: 'FPC_SETUPREADSTR_ANSISTR']; compilerproc;
begin
  SetupReadStrCommon(TextRec(ReadWriteStrText),StringCodePage(s));
  { we need a reference, because 's' may be a temporary expression }
  PAnsiString(@TextRec(ReadWriteStrText).userdata[StrPtrIndex])^:=s;
  TextRec(ReadWriteStrText).InOutFunc:=@ReadStrAnsi;
  { this is called at the end, by fpc_read_end }
  TextRec(ReadWriteStrText).FlushFunc:=@ReadAnsiStrFinal;
end;

procedure fpc_SetupReadStr_Ansistr_Intern(var ReadWriteStrText: text; const s: rawbytestring); [external name 'FPC_SETUPREADSTR_ANSISTR'];
{$endif FPC_HAS_FEATURE_ANSISTRINGS}


procedure fpc_SetupReadStr_Shortstr(var ReadWriteStrText: text; const s: shortstring); compilerproc;
begin
  { the reason we convert the short string to ansistring, is because the semantics of
    readstr are defined as:

    *********************
    Apart from the restrictions imposed by requirements given in this clause,
    the execution of readstr(e,v 1 ,...,v n ) where e denotes a
    string-expression and v 1 ,...,v n denote variable-accesses possessing the
    char-type (or a subrange of char-type), the integer-type (or a subrange of
    integer-type), the real-type, a fixed-string-type, or a
    variable-string-type, shall be equivalent to

            begin
            rewrite(f);
            writeln(f, e);
            reset(f);
            read(f, v 1 ,...,v n )
            end
    *********************

    This means that any side effects caused by the evaluation of v 1 .. v n
    must not affect the value of e (= our argument s) -> we need a copy of it.
    An ansistring is the easiest way to get a threadsafe copy, and allows us
    to use the other ansistring readstr helpers too.
  }
{$ifdef FPC_HAS_FEATURE_ANSISTRINGS}
  fpc_SetupReadStr_Ansistr_Intern(ReadWriteStrText,s);
{$else FPC_HAS_FEATURE_ANSISTRINGS}
  runerror(217);
{$endif FPC_HAS_FEATURE_ANSISTRINGS}
end;


{$ifdef FPC_HAS_FEATURE_WIDESTRINGS}
procedure fpc_SetupReadStr_Unicodestr(var ReadWriteStrText: text; const s: unicodestring); compilerproc;
begin
  { we use an utf8string to avoid code duplication }
  fpc_SetupReadStr_Ansistr_Intern(ReadWriteStrText,utf8string(s));
end;
{$endif FPC_HAS_FEATURE_WIDESTRINGS}


{$ifndef FPC_WIDESTRING_EQUAL_UNICODESTRING}
procedure fpc_SetupReadStr_Widestr(var ReadWriteStrText: text; const s: widestring); compilerproc;
begin
  { we use an utf8string to avoid code duplication }
  fpc_SetupReadStr_Ansistr_Intern(ReadWriteStrText,utf8string(s));
end;
{$endif FPC_WIDESTRING_EQUAL_UNICODESTRING}


{*****************************************************************************
                               Initializing
*****************************************************************************}

procedure OpenStdIO(var f:text;mode:longint;hdl:thandle);
begin
  Assign(f,'');
  TextRec(f).Handle:=hdl;
  TextRec(f).Mode:=mode;
  TextRec(f).Closefunc:=@FileCloseFunc;
  case mode of
    fmInput :
      begin
        TextRec(f).InOutFunc:=@FileReadFunc;
      {$if defined(FPC_HAS_CPSTRING) and defined(FPC_HAS_FEATURE_WIDESTRINGS)}
        TextRec(f).CodePage:=WideStringManager.GetStandardCodePageProc(scpConsoleInput);
      {$endif}
      end;
    fmOutput :
      begin
        TextRec(f).InOutFunc:=@FileWriteFunc;
        {$if defined(FPC_HAS_CPSTRING) and defined(FPC_HAS_FEATURE_WIDESTRINGS)}
        TextRec(f).CodePage:=WideStringManager.GetStandardCodePageProc(scpConsoleOutput);
      {$endif}
        if Do_Isdevice(hdl) then
          TextRec(f).FlushFunc:=@FileWriteFunc;
      end;
  else
   HandleError(102);
  end;
end;


