123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 2005 by Free Pascal development team
- Low level file functions
- 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.
- **********************************************************************}
- { Enable this for file handling debug }
- {DEFINE ASYS_FPC_FILEDEBUG}
- {*****************************************************************************
- File-handling Support Functions
- *****************************************************************************}
- type
- { AmigaOS does not automatically close opened files on exit back to }
- { the operating system, therefore as a precuation we close all files }
- { manually on exit. }
- PFileList = ^TFileList;
- TFileList = record { no packed, must be correctly aligned }
- handle : THandle; { Handle to file }
- next : PFileList; { Next file in list }
- buffered : boolean; { used buffered I/O? }
- end;
- var
- ASYS_fileList: PFileList; public name 'ASYS_FILELIST'; { List pointer to opened files }
- { Function to be called at program shutdown, to close all opened files }
- procedure CloseList(l: PFileList);
- var
- tmpNext : PFileList;
- tmpHandle : THandle;
- begin
- if l=nil then exit;
- ObtainSemaphore(ASYS_fileSemaphore);
- { First, close all tracked files }
- tmpNext:=l^.next;
- while tmpNext<>nil do begin
- tmpHandle:=tmpNext^.handle;
- if (tmpHandle<>StdInputHandle) and (tmpHandle<>StdOutputHandle)
- and (tmpHandle<>StdErrorHandle) then begin
- dosClose(tmpHandle);
- end;
- tmpNext:=tmpNext^.next;
- end;
- { Next, erase the linked list }
- while l<>nil do begin
- tmpNext:=l;
- l:=l^.next;
- FreePooled(ASYS_heapPool, tmpNext, SizeOf(TFileList));
- end;
- ReleaseSemaphore(ASYS_fileSemaphore);
- end;
- { Function to be called to add a file to the opened file list }
- procedure AddToList(var l: PFileList; h: THandle); alias: 'ADDTOLIST'; [public];
- var
- p : PFileList;
- inList: Boolean;
- begin
- inList:=False;
- ObtainSemaphore(ASYS_fileSemaphore);
- if l<>nil then begin
- { if there is a valid filelist, search for the value }
- { in the list to avoid double additions }
- p:=l;
- while (p^.next<>nil) and (not inList) do
- if p^.next^.handle=h then inList:=True
- else p:=p^.next;
- p:=nil;
- end else begin
- { if the list is not yet allocated, allocate it.
- the FileList is only freed after the memory manager has shut
- down, therefore native OS allocation }
- l := AllocPooled(ASYS_heapPool,sizeof(TFileList));
- l^.next:=nil;
- end;
- if not inList then begin
- P := AllocPooled(ASYS_heapPool,sizeof(TFileList));
- p^.handle:=h;
- p^.buffered:=False;
- p^.next:=l^.next;
- l^.next:=p;
- end
- {$IFDEF ASYS_FPC_FILEDEBUG}
- else
- RawDoFmt('FPC_FILE_DEBUG: Error! Trying add filehandle a filehandle twice: $%lx !'+#10,@h,pointer(1),nil);
- {$ENDIF}
- ;
- ReleaseSemaphore(ASYS_fileSemaphore);
- end;
- { Function to be called to remove a file from the list }
- function RemoveFromList(var l: PFileList; h: THandle): boolean; alias: 'REMOVEFROMLIST'; [public];
- var
- p : PFileList;
- inList : Boolean;
- tmpList: PFileList;
- begin
- inList:=False;
- if l=nil then begin
- RemoveFromList:=inList;
- exit;
- end;
- ObtainSemaphore(ASYS_fileSemaphore);
- p:=l;
- while (p^.next<>nil) and (not inList) do
- if p^.next^.handle=h then inList:=True
- else p:=p^.next;
- if inList then begin
- tmpList:=p^.next^.next;
- FreePooled(ASYS_heapPool, p^.next, SizeOf(TFileList));
- p^.next:=tmpList;
- end
- {$IFDEF ASYS_FPC_FILEDEBUG}
- else
- RawDoFmt('FPC_FILE_DEBUG: Error! Trying to remove not existing filehandle: $%lx !'+#10,@h,pointer(1),nil);
- {$ENDIF}
- ;
- ReleaseSemaphore(ASYS_fileSemaphore);
- RemoveFromList:=inList;
- end;
- { Function to check if file is in the list }
- function CheckInList(var l: PFileList; h: THandle): pointer; alias: 'CHECKINLIST'; [public];
- var
- p : PFileList;
- inList : Pointer;
- begin
- inList:=nil;
- if l=nil then begin
- CheckInList:=inList;
- exit;
- end;
- ObtainSemaphore(ASYS_fileSemaphore);
- p:=l;
- while (p^.next<>nil) and (inList=nil) do
- if p^.next^.handle=h then inList:=p^.next
- else p:=p^.next;
- {$IFDEF ASYS_FPC_FILEDEBUG}
- if inList=nil then
- RawDoFmt('FPC_FILE_DEBUG: Warning! Check for not existing filehandle: $%lx !'+#10,@h,pointer(1),nil);
- {$ENDIF}
- ReleaseSemaphore(ASYS_fileSemaphore);
- CheckInList:=inList;
- end;
- {****************************************************************************
- Low level File Routines
- All these functions can set InOutRes on errors
- ****************************************************************************}
- { close a file from the handle value }
- procedure do_close(handle : THandle);
- begin
- if RemoveFromList(ASYS_fileList,handle) then begin
- { Do _NOT_ check CTRL_C on Close, because it will conflict
- with System_Exit! }
- if not dosClose(handle) then
- dosError2InOut(IoErr);
- end;
- end;
- procedure do_erase(p : pchar; pchangeable: boolean);
- var
- tmpStr: array[0..255] of Char;
- begin
- tmpStr:=PathConv(strpas(p))+#0;
- checkCTRLC;
- if not dosDeleteFile(@tmpStr) then
- dosError2InOut(IoErr);
- end;
- procedure do_rename(p1,p2 : pchar; p1changeable, p2changeable: boolean);
- { quite stack-effective code, huh? :) damn path conversions... (KB) }
- var
- tmpStr1: array[0..255] of Char;
- tmpStr2: array[0..255] of Char;
- begin
- tmpStr1:=PathConv(strpas(p1))+#0;
- tmpStr2:=PathConv(strpas(p2))+#0;
- checkCTRLC;
- if not (dosRename(@tmpStr1,@tmpStr2) <> 0) then
- dosError2InOut(IoErr);
- end;
- function do_write(h: THandle; addr: pointer; len: longint) : longint;
- var dosResult: LongInt;
- begin
- checkCTRLC;
- do_write:=0;
- if (len<=0) or (h=0) or (h=-1) then exit;
- {$IFDEF ASYS_FPC_FILEDEBUG}
- if not ((h=StdOutputHandle) or (h=StdInputHandle) or
- (h=StdErrorHandle)) then CheckInList(ASYS_fileList,h);
- {$ENDIF}
- dosResult:=dosWrite(h,addr,len);
- if dosResult<0 then begin
- dosError2InOut(IoErr);
- end else begin
- do_write:=dosResult;
- end;
- end;
- function do_read(h: THandle; addr: pointer; len: longint) : longint;
- var dosResult: LongInt;
- begin
- checkCTRLC;
- do_read:=0;
- if (len<=0) or (h=0) or (h=-1) then exit;
- {$IFDEF ASYS_FPC_FILEDEBUG}
- if not ((h=StdOutputHandle) or (h=StdInputHandle) or
- (h=StdErrorHandle)) then CheckInList(ASYS_fileList,h);
- {$ENDIF}
- dosResult:=dosRead(h,addr,len);
- if dosResult<0 then begin
- dosError2InOut(IoErr);
- end else begin
- do_read:=dosResult;
- end
- end;
- function do_filepos(handle: THandle) : longint;
- var dosResult: LongInt;
- begin
- checkCTRLC;
- do_filepos:=-1;
- if CheckInList(ASYS_fileList,handle)<>nil then begin
- { Seeking zero from OFFSET_CURRENT to find out where we are }
- dosResult:=dosSeek(handle,0,OFFSET_CURRENT);
- if dosResult<0 then begin
- dosError2InOut(IoErr);
- end else begin
- do_filepos:=dosResult;
- end;
- end;
- end;
- procedure do_seek(handle: THandle; pos: longint);
- begin
- checkCTRLC;
- if CheckInList(ASYS_fileList,handle)<>nil then begin
- { Seeking from OFFSET_BEGINNING }
- if dosSeek(handle,pos,OFFSET_BEGINNING)<0 then
- dosError2InOut(IoErr);
- end;
- end;
- function do_seekend(handle: THandle):longint;
- var dosResult: LongInt;
- begin
- checkCTRLC;
- do_seekend:=-1;
- if CheckInList(ASYS_fileList,handle)<>nil then begin
- { Seeking to OFFSET_END }
- dosResult:=dosSeek(handle,0,OFFSET_END);
- if dosResult<0 then begin
- dosError2InOut(IoErr);
- end else begin
- do_seekend:=dosSeek(handle,0,OFFSET_CURRENT);
- end;
- end;
- end;
- {$DEFINE ASYS_FILESIZE_USE_EXAMINEFH}
- { I changed the double-Seek filesize method which we
- were using for 10+ years to the new ExamineFH() method.
- It should be available AmigaOS 2.0+, and much faster.
- (I actually measured several magnitudes of improvement,
- especially on large files.)
- It should be safe since there are several libc implementations
- using the same method on all Amiga flavors, but if anyone has
- a problem with it, disable this define to revert to the old
- method and report the issue. (KB)
- Actually, as some file systems (FTPMount, probably more) do
- not support the ExamineFH action, lets fall back to double
- seek silently in that case (KB) }
- function do_filesize(handle : THandle) : longint;
- var
- {$IFDEF ASYS_FILESIZE_USE_EXAMINEFH}
- fib: PFileInfoBlock;
- {$ENDIF}
- currfilepos: longint;
- begin
- checkCTRLC;
- do_filesize:=-1;
- if CheckInList(ASYS_fileList,handle)<>nil then begin
- {$IFDEF ASYS_FILESIZE_USE_EXAMINEFH}
- fib:=AllocDosObject(DOS_FIB,nil);
- if fib <> nil then begin
- if ExamineFH(BPTR(handle), fib) then
- do_filesize:=fib^.fib_Size;
- FreeDosObject(DOS_FIB,fib);
- end;
- {$ENDIF}
- if do_filesize = -1 then
- begin
- currfilepos:=do_filepos(handle);
- do_filesize:=do_seekend(handle);
- do_seek(handle,currfilepos);
- end;
- end;
- end;
- { truncate at a given position }
- procedure do_truncate(handle: THandle; pos: longint);
- begin
- checkCTRLC;
- if CheckInList(ASYS_fileList,handle)<>nil then begin
- { Seeking from OFFSET_BEGINNING }
- if SetFileSize(handle,pos,OFFSET_BEGINNING)<0 then
- dosError2InOut(IoErr);
- end;
- end;
- procedure do_open(var f;p:pchar;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 $10) the file will be append
- when (flags and $100) the file will be truncate/rewritten
- when (flags and $1000) there is no check for close (needed for textfiles)
- }
- var
- handle : THandle;
- openflags: LongInt;
- tmpStr : array[0..255] of Char;
- begin
- tmpStr:=PathConv(strpas(p))+#0;
- { 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 }
- { READ/WRITE on existing file }
- { RESET/APPEND }
- openflags:=MODE_OLDFILE;
- case (flags and 3) of
- 0 : filerec(f).mode:=fminput;
- 1 : filerec(f).mode:=fmoutput;
- 2 : filerec(f).mode:=fminout;
- end;
- { rewrite (create a new file) }
- if (flags and $1000)<>0 then openflags:=MODE_NEWFILE;
- { 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;
- handle:=Open(@tmpStr,openflags);
- if handle=0 then begin
- begin
- dosError2InOut(IoErr);
- FileRec(f).mode:=fmclosed;
- end
- end else begin
- AddToList(ASYS_fileList,handle);
- filerec(f).handle:=handle;
- end;
- { 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
- if (handle=StdOutputHandle) or (handle=StdInputHandle) or
- (handle=StdErrorHandle) then
- do_isdevice:=True
- else
- do_isdevice:=False;
- end;
|