{ This file is part of the Free Pascal run time library. Copyright (c) 2020 by Free Pascal development team Low level file functions for the Sinclair QL 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. **********************************************************************} {**************************************************************************** Low level File Routines All these functions can set InOutRes on errors ****************************************************************************} { close a file from the handle value } procedure do_close(handle : longint); begin Error2InOutRes(io_close(handle)); end; { delete a file, given its name } procedure do_erase(p : PAnsiChar; pchangeable: boolean); begin Error2InOutRes(io_delet(p)); end; procedure do_rename(p1,p2 : PAnsiChar; p1changeable, p2changeable: boolean); var chanId: longint; res: longint; begin { To rename a QL file, it must exist and be opened. For WIN/FLP this means open mode 0 (Q_OPEN) but for RAM this can be any of Q_OPEN, Q_OPEN_NEW or Q_OPEN_OVER. } { Does the file exist? } chanId := io_open(p1, Q_OPEN_IN); if chanId < 0 then begin InOutRes:=2; { File not found. } exit; end; { Close and reopen in correct mode. } io_close(chanId); chanId := io_open(p1, Q_OPEN); if chanId < 0 then begin Error2InOutRes(chanId); exit; end; { Now, finally, we can rename. } res := fs_rename(chanId,p2); { Close the file. Never errors out. } io_close(chanId); if res < 0 then Error2InOutRes(res); end; function do_write(h: longint; addr: pointer; len: longint) : longint; var res: longint; begin do_write:=0; res:=io_sstrg(h, -1, addr, len); if res < 0 then Error2InOutRes(res) else do_write:=res; end; function do_read(h: longint; addr: pointer; len: longint) : longint; var res: longint; begin do_read := 0; res := io_fline(h, -1, addr, len); if res = ERR_EF then res := 0; if res < 0 then Error2InOutRes(res) else do_read := res; end; function do_filepos(handle: longint): longint; var res: longint; pos: longint; begin do_filepos := 0; pos := 0; res := fs_posre(handle, pos); if res = ERR_EF then res := 0; if (res < 0) then Error2InOutRes(res) else do_filepos := pos; end; procedure do_seek(handle, pos: longint); var res: longint; begin res := fs_posab(handle, pos); if res = ERR_EF then res := 0; if (res < 0) then Error2InOutRes(res); end; { The maximum length of a QL file is 2^31 - 64 bytes ($7FFFFFC0) so the maximum offset is that, minus 1. ($7fffffBF) } const MAX_QL_FILE_LENGTH = $7FFFFFBF; function do_seekend(handle: longint): longint; var res: longint; pos: longint; begin do_seekend:=-1; pos:=MAX_QL_FILE_LENGTH; res:=fs_posab(handle, pos); if res = ERR_EF then res := 0; if res < 0 then Error2InOutRes(res) else do_seekend := pos; end; function do_filesize(handle: longint): longint; var res: longint; header: array [0..$39] of byte; begin do_filesize := 0; res := fs_headr(handle, @header, $40); if res < 0 then Error2InOutRes(res) else do_filesize := plongint(@header[0])^; end; { truncate at a given position } procedure do_truncate(handle, pos: longint); begin do_seek(handle, pos); fs_truncate(handle); end; procedure do_open(var f;p:PAnsiChar;flags:longint; pchangeable: boolean); { filerec and textrec have both handle and mode as the first items so they could use the same routine for opening/creating. when (flags and $100) the file will be append when (flags and $1000) the file will be truncate/rewritten when (flags and $10000) there is no check for close (needed for textfiles) } var res: longint; openMode: longint; begin openMode:=Q_OPEN; { close first if opened } if ((flags and $10000)=0) then begin case filerec(f).mode of fmInput, fmOutput, fmInout: do_close(filerec(f).handle); fmClosed: ; else begin InOutRes:=102; {not assigned} exit; end; end; end; { reset file handle } filerec(f).handle:=UnusedHandle; { convert filemode to filerec modes } case (flags and 3) of 0 : filerec(f).mode:=fmInput; 1 : filerec(f).mode:=fmOutput; 2 : filerec(f).mode:=fmInout; end; { empty name is special } if p[0]=#0 then begin case filerec(f).mode of fminput : filerec(f).handle:=StdInputHandle; fmappend, fmoutput : begin filerec(f).handle:=StdOutputHandle; filerec(f).mode:=fmOutput; {fool fmappend} end; end; exit; end; { rewrite (create a new file) } if (flags and $1000)<>0 then openMode:=Q_OPEN_OVER; res:=io_open(p,openMode); if res < 0 then begin Error2InOutRes(res); filerec(f).mode:=fmClosed; exit; end else filerec(f).handle:=res; { append mode } if ((Flags and $100)<>0) and (FileRec(F).Handle<>UnusedHandle) then begin do_seekend(filerec(f).handle); filerec(f).mode:=fmOutput; {fool fmappend} end; end; function do_isdevice(handle: thandle): boolean; begin { FIXME: See if this can be implemented properly on the QL. } { Prefer to return true here as a default answer, as it is less harmful than false. This basically determines if the file handle is a "device", for example the console. Returning true here causes a flush before a read on the file handle which is preferred for consoleio, and a few other minor behavioral changes, for example shorter stacktraces. Returning false will cause weird behavior and unprinted lines when read() and write() is mixed during consoleio. } do_isdevice:=true; end;