2
0

sysfile.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2005 by Free Pascal development team
  4. Low level file functions
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. { Enable this for file handling debug }
  12. {DEFINE ASYS_FPC_FILEDEBUG}
  13. {*****************************************************************************
  14. File-handling Support Functions
  15. *****************************************************************************}
  16. type
  17. { AmigaOS does not automatically close opened files on exit back to }
  18. { the operating system, therefore as a precuation we close all files }
  19. { manually on exit. }
  20. PFileList = ^TFileList;
  21. TFileList = record { no packed, must be correctly aligned }
  22. handle : THandle; { Handle to file }
  23. next : PFileList; { Next file in list }
  24. buffered : boolean; { used buffered I/O? }
  25. end;
  26. var
  27. ASYS_fileList: PFileList; public name 'ASYS_FILELIST'; { List pointer to opened files }
  28. { Function to be called at program shutdown, to close all opened files }
  29. procedure CloseList(l: PFileList);
  30. var
  31. tmpNext : PFileList;
  32. tmpHandle : THandle;
  33. begin
  34. if l=nil then exit;
  35. ObtainSemaphore(ASYS_fileSemaphore);
  36. { First, close all tracked files }
  37. tmpNext:=l^.next;
  38. while tmpNext<>nil do begin
  39. tmpHandle:=tmpNext^.handle;
  40. if (tmpHandle<>StdInputHandle) and (tmpHandle<>StdOutputHandle)
  41. and (tmpHandle<>StdErrorHandle) then begin
  42. dosClose(tmpHandle);
  43. end;
  44. tmpNext:=tmpNext^.next;
  45. end;
  46. { Next, erase the linked list }
  47. while l<>nil do begin
  48. tmpNext:=l;
  49. l:=l^.next;
  50. FreePooled(ASYS_heapPool, tmpNext, SizeOf(TFileList));
  51. end;
  52. ReleaseSemaphore(ASYS_fileSemaphore);
  53. end;
  54. { Function to be called to add a file to the opened file list }
  55. procedure AddToList(var l: PFileList; h: THandle); alias: 'ADDTOLIST'; [public];
  56. var
  57. p : PFileList;
  58. inList: Boolean;
  59. begin
  60. inList:=False;
  61. ObtainSemaphore(ASYS_fileSemaphore);
  62. if l<>nil then begin
  63. { if there is a valid filelist, search for the value }
  64. { in the list to avoid double additions }
  65. p:=l;
  66. while (p^.next<>nil) and (not inList) do
  67. if p^.next^.handle=h then inList:=True
  68. else p:=p^.next;
  69. p:=nil;
  70. end else begin
  71. { if the list is not yet allocated, allocate it.
  72. the FileList is only freed after the memory manager has shut
  73. down, therefore native OS allocation }
  74. l := AllocPooled(ASYS_heapPool,sizeof(TFileList));
  75. l^.next:=nil;
  76. end;
  77. if not inList then begin
  78. P := AllocPooled(ASYS_heapPool,sizeof(TFileList));
  79. p^.handle:=h;
  80. p^.buffered:=False;
  81. p^.next:=l^.next;
  82. l^.next:=p;
  83. end
  84. {$IFDEF ASYS_FPC_FILEDEBUG}
  85. else
  86. RawDoFmt('FPC_FILE_DEBUG: Error! Trying add filehandle a filehandle twice: $%lx !'+#10,@h,pointer(1),nil);
  87. {$ENDIF}
  88. ;
  89. ReleaseSemaphore(ASYS_fileSemaphore);
  90. end;
  91. { Function to be called to remove a file from the list }
  92. function RemoveFromList(var l: PFileList; h: THandle): boolean; alias: 'REMOVEFROMLIST'; [public];
  93. var
  94. p : PFileList;
  95. inList : Boolean;
  96. tmpList: PFileList;
  97. begin
  98. inList:=False;
  99. if l=nil then begin
  100. RemoveFromList:=inList;
  101. exit;
  102. end;
  103. ObtainSemaphore(ASYS_fileSemaphore);
  104. p:=l;
  105. while (p^.next<>nil) and (not inList) do
  106. if p^.next^.handle=h then inList:=True
  107. else p:=p^.next;
  108. if inList then begin
  109. tmpList:=p^.next^.next;
  110. FreePooled(ASYS_heapPool, p^.next, SizeOf(TFileList));
  111. p^.next:=tmpList;
  112. end
  113. {$IFDEF ASYS_FPC_FILEDEBUG}
  114. else
  115. RawDoFmt('FPC_FILE_DEBUG: Error! Trying to remove not existing filehandle: $%lx !'+#10,@h,pointer(1),nil);
  116. {$ENDIF}
  117. ;
  118. ReleaseSemaphore(ASYS_fileSemaphore);
  119. RemoveFromList:=inList;
  120. end;
  121. { Function to check if file is in the list }
  122. function CheckInList(var l: PFileList; h: THandle): pointer; alias: 'CHECKINLIST'; [public];
  123. var
  124. p : PFileList;
  125. inList : Pointer;
  126. begin
  127. inList:=nil;
  128. if l=nil then begin
  129. CheckInList:=inList;
  130. exit;
  131. end;
  132. ObtainSemaphore(ASYS_fileSemaphore);
  133. p:=l;
  134. while (p^.next<>nil) and (inList=nil) do
  135. if p^.next^.handle=h then inList:=p^.next
  136. else p:=p^.next;
  137. {$IFDEF ASYS_FPC_FILEDEBUG}
  138. if inList=nil then
  139. RawDoFmt('FPC_FILE_DEBUG: Warning! Check for not existing filehandle: $%lx !'+#10,@h,pointer(1),nil);
  140. {$ENDIF}
  141. ReleaseSemaphore(ASYS_fileSemaphore);
  142. CheckInList:=inList;
  143. end;
  144. {****************************************************************************
  145. Low level File Routines
  146. All these functions can set InOutRes on errors
  147. ****************************************************************************}
  148. { close a file from the handle value }
  149. procedure do_close(handle : THandle);
  150. begin
  151. if RemoveFromList(ASYS_fileList,handle) then begin
  152. { Do _NOT_ check CTRL_C on Close, because it will conflict
  153. with System_Exit! }
  154. if not dosClose(handle) then
  155. dosError2InOut(IoErr);
  156. end;
  157. end;
  158. procedure do_erase(p : pchar; pchangeable: boolean);
  159. var
  160. tmpStr: array[0..255] of Char;
  161. begin
  162. tmpStr:=PathConv(strpas(p))+#0;
  163. checkCTRLC;
  164. if not dosDeleteFile(@tmpStr) then
  165. dosError2InOut(IoErr);
  166. end;
  167. procedure do_rename(p1,p2 : pchar; p1changeable, p2changeable: boolean);
  168. { quite stack-effective code, huh? :) damn path conversions... (KB) }
  169. var
  170. tmpStr1: array[0..255] of Char;
  171. tmpStr2: array[0..255] of Char;
  172. begin
  173. tmpStr1:=PathConv(strpas(p1))+#0;
  174. tmpStr2:=PathConv(strpas(p2))+#0;
  175. checkCTRLC;
  176. if not (dosRename(@tmpStr1,@tmpStr2) <> 0) then
  177. dosError2InOut(IoErr);
  178. end;
  179. function do_write(h: THandle; addr: pointer; len: longint) : longint;
  180. var dosResult: LongInt;
  181. begin
  182. checkCTRLC;
  183. do_write:=0;
  184. if (len<=0) or (h=0) or (h=-1) then exit;
  185. {$IFDEF ASYS_FPC_FILEDEBUG}
  186. if not ((h=StdOutputHandle) or (h=StdInputHandle) or
  187. (h=StdErrorHandle)) then CheckInList(ASYS_fileList,h);
  188. {$ENDIF}
  189. dosResult:=dosWrite(h,addr,len);
  190. if dosResult<0 then begin
  191. dosError2InOut(IoErr);
  192. end else begin
  193. do_write:=dosResult;
  194. end;
  195. end;
  196. function do_read(h: THandle; addr: pointer; len: longint) : longint;
  197. var dosResult: LongInt;
  198. begin
  199. checkCTRLC;
  200. do_read:=0;
  201. if (len<=0) or (h=0) or (h=-1) then exit;
  202. {$IFDEF ASYS_FPC_FILEDEBUG}
  203. if not ((h=StdOutputHandle) or (h=StdInputHandle) or
  204. (h=StdErrorHandle)) then CheckInList(ASYS_fileList,h);
  205. {$ENDIF}
  206. dosResult:=dosRead(h,addr,len);
  207. if dosResult<0 then begin
  208. dosError2InOut(IoErr);
  209. end else begin
  210. do_read:=dosResult;
  211. end
  212. end;
  213. function do_filepos(handle: THandle) : longint;
  214. var dosResult: LongInt;
  215. begin
  216. checkCTRLC;
  217. do_filepos:=-1;
  218. if CheckInList(ASYS_fileList,handle)<>nil then begin
  219. { Seeking zero from OFFSET_CURRENT to find out where we are }
  220. dosResult:=dosSeek(handle,0,OFFSET_CURRENT);
  221. if dosResult<0 then begin
  222. dosError2InOut(IoErr);
  223. end else begin
  224. do_filepos:=dosResult;
  225. end;
  226. end;
  227. end;
  228. procedure do_seek(handle: THandle; pos: longint);
  229. begin
  230. checkCTRLC;
  231. if CheckInList(ASYS_fileList,handle)<>nil then begin
  232. { Seeking from OFFSET_BEGINNING }
  233. if dosSeek(handle,pos,OFFSET_BEGINNING)<0 then
  234. dosError2InOut(IoErr);
  235. end;
  236. end;
  237. function do_seekend(handle: THandle):longint;
  238. var dosResult: LongInt;
  239. begin
  240. checkCTRLC;
  241. do_seekend:=-1;
  242. if CheckInList(ASYS_fileList,handle)<>nil then begin
  243. { Seeking to OFFSET_END }
  244. dosResult:=dosSeek(handle,0,OFFSET_END);
  245. if dosResult<0 then begin
  246. dosError2InOut(IoErr);
  247. end else begin
  248. do_seekend:=dosSeek(handle,0,OFFSET_CURRENT);
  249. end;
  250. end;
  251. end;
  252. {$DEFINE ASYS_FILESIZE_USE_EXAMINEFH}
  253. { I changed the double-Seek filesize method which we
  254. were using for 10+ years to the new ExamineFH() method.
  255. It should be available AmigaOS 2.0+, and much faster.
  256. (I actually measured several magnitudes of improvement,
  257. especially on large files.)
  258. It should be safe since there are several libc implementations
  259. using the same method on all Amiga flavors, but if anyone has
  260. a problem with it, disable this define to revert to the old
  261. method and report the issue. (KB)
  262. Actually, as some file systems (FTPMount, probably more) do
  263. not support the ExamineFH action, lets fall back to double
  264. seek silently in that case (KB) }
  265. function do_filesize(handle : THandle) : longint;
  266. var
  267. {$IFDEF ASYS_FILESIZE_USE_EXAMINEFH}
  268. fib: PFileInfoBlock;
  269. {$ENDIF}
  270. currfilepos: longint;
  271. begin
  272. checkCTRLC;
  273. do_filesize:=-1;
  274. if CheckInList(ASYS_fileList,handle)<>nil then begin
  275. {$IFDEF ASYS_FILESIZE_USE_EXAMINEFH}
  276. fib:=AllocDosObject(DOS_FIB,nil);
  277. if fib <> nil then begin
  278. if ExamineFH(BPTR(handle), fib) then
  279. do_filesize:=fib^.fib_Size;
  280. FreeDosObject(DOS_FIB,fib);
  281. end;
  282. {$ENDIF}
  283. if do_filesize = -1 then
  284. begin
  285. currfilepos:=do_filepos(handle);
  286. do_filesize:=do_seekend(handle);
  287. do_seek(handle,currfilepos);
  288. end;
  289. end;
  290. end;
  291. { truncate at a given position }
  292. procedure do_truncate(handle: THandle; pos: longint);
  293. begin
  294. checkCTRLC;
  295. if CheckInList(ASYS_fileList,handle)<>nil then begin
  296. { Seeking from OFFSET_BEGINNING }
  297. if SetFileSize(handle,pos,OFFSET_BEGINNING)<0 then
  298. dosError2InOut(IoErr);
  299. end;
  300. end;
  301. procedure do_open(var f;p:pchar;flags:longint; pchangeable: boolean);
  302. {
  303. filerec and textrec have both handle and mode as the first items so
  304. they could use the same routine for opening/creating.
  305. when (flags and $10) the file will be append
  306. when (flags and $100) the file will be truncate/rewritten
  307. when (flags and $1000) there is no check for close (needed for textfiles)
  308. }
  309. var
  310. handle : THandle;
  311. openflags: LongInt;
  312. tmpStr : array[0..255] of Char;
  313. begin
  314. tmpStr:=PathConv(strpas(p))+#0;
  315. { close first if opened }
  316. if ((flags and $10000)=0) then begin
  317. case filerec(f).mode of
  318. fminput,fmoutput,fminout : Do_Close(filerec(f).handle);
  319. fmclosed : ;
  320. else begin
  321. inoutres:=102; {not assigned}
  322. exit;
  323. end;
  324. end;
  325. end;
  326. { reset file handle }
  327. filerec(f).handle:=UnusedHandle;
  328. { convert filemode to filerec modes }
  329. { READ/WRITE on existing file }
  330. { RESET/APPEND }
  331. openflags:=MODE_OLDFILE;
  332. case (flags and 3) of
  333. 0 : filerec(f).mode:=fminput;
  334. 1 : filerec(f).mode:=fmoutput;
  335. 2 : filerec(f).mode:=fminout;
  336. end;
  337. { rewrite (create a new file) }
  338. if (flags and $1000)<>0 then openflags:=MODE_NEWFILE;
  339. { empty name is special }
  340. if p[0]=#0 then begin
  341. case filerec(f).mode of
  342. fminput :
  343. filerec(f).handle:=StdInputHandle;
  344. fmappend,
  345. fmoutput : begin
  346. filerec(f).handle:=StdOutputHandle;
  347. filerec(f).mode:=fmoutput; {fool fmappend}
  348. end;
  349. end;
  350. exit;
  351. end;
  352. handle:=Open(@tmpStr,openflags);
  353. if handle=0 then begin
  354. begin
  355. dosError2InOut(IoErr);
  356. FileRec(f).mode:=fmclosed;
  357. end
  358. end else begin
  359. AddToList(ASYS_fileList,handle);
  360. filerec(f).handle:=handle;
  361. end;
  362. { append mode }
  363. if ((Flags and $100)<>0) and
  364. (FileRec(F).Handle<>UnusedHandle) then begin
  365. do_seekend(filerec(f).handle);
  366. filerec(f).mode:=fmoutput; {fool fmappend}
  367. end;
  368. end;
  369. function do_isdevice(handle: THandle): boolean;
  370. begin
  371. if (handle=StdOutputHandle) or (handle=StdInputHandle) or
  372. (handle=StdErrorHandle) then
  373. do_isdevice:=True
  374. else
  375. do_isdevice:=False;
  376. end;