sysutils.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by Florian Klaempfl
  5. member of the Free Pascal development team
  6. Sysutils unit for linux
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. unit sysutils;
  14. interface
  15. {$MODE objfpc}
  16. { force ansistrings }
  17. {$H+}
  18. {$DEFINE HAS_SLEEP}
  19. {$DEFINE HAS_OSERROR}
  20. uses
  21. Unix,errors,sysconst;
  22. { Include platform independent interface part }
  23. {$i sysutilh.inc}
  24. implementation
  25. Uses UnixUtil,Baseunix,UnixType;
  26. {$Define OS_FILEISREADONLY} // Specific implementation for Unix.
  27. { Include platform independent implementation part }
  28. {$i sysutils.inc}
  29. {****************************************************************************
  30. File Functions
  31. ****************************************************************************}
  32. Function FileOpen (Const FileName : string; Mode : Integer) : Longint;
  33. Var LinuxFlags : longint;
  34. BEGIN
  35. LinuxFlags:=0;
  36. Case (Mode and 3) of
  37. 0 : LinuxFlags:=LinuxFlags or O_RdOnly;
  38. 1 : LinuxFlags:=LinuxFlags or O_WrOnly;
  39. 2 : LinuxFlags:=LinuxFlags or O_RdWr;
  40. end;
  41. FileOpen:=fpOpen (FileName,LinuxFlags);
  42. //!! We need to set locking based on Mode !!
  43. end;
  44. Function FileCreate (Const FileName : String) : Longint;
  45. begin
  46. FileCreate:=fpOpen(FileName,O_RdWr or O_Creat or O_Trunc);
  47. end;
  48. Function FileCreate (Const FileName : String;Mode : Longint) : Longint;
  49. Var LinuxFlags : longint;
  50. BEGIN
  51. LinuxFlags:=0;
  52. Case (Mode and 3) of
  53. 0 : LinuxFlags:=LinuxFlags or O_RdOnly;
  54. 1 : LinuxFlags:=LinuxFlags or O_WrOnly;
  55. 2 : LinuxFlags:=LinuxFlags or O_RdWr;
  56. end;
  57. FileCreate:=fpOpen(FileName,LinuxFlags or O_Creat or O_Trunc);
  58. end;
  59. Function FileRead (Handle : Longint; Var Buffer; Count : longint) : Longint;
  60. begin
  61. FileRead:=fpRead (Handle,Buffer,Count);
  62. end;
  63. Function FileWrite (Handle : Longint; const Buffer; Count : Longint) : Longint;
  64. begin
  65. FileWrite:=fpWrite (Handle,Buffer,Count);
  66. end;
  67. Function FileSeek (Handle,FOffset,Origin : Longint) : Longint;
  68. begin
  69. FileSeek:=fplSeek (Handle,FOffset,Origin);
  70. end;
  71. Function FileSeek (Handle : Longint; FOffset,Origin : Int64) : Int64;
  72. begin
  73. {$warning need to add 64bit call }
  74. FileSeek:=fplSeek (Handle,FOffset,Origin);
  75. end;
  76. Procedure FileClose (Handle : Longint);
  77. begin
  78. fpclose(Handle);
  79. end;
  80. Function FileTruncate (Handle,Size: Longint) : boolean;
  81. begin
  82. FileTruncate:=fpftruncate(Handle,Size)>=0;
  83. end;
  84. Function FileAge (Const FileName : String): Longint;
  85. Var Info : Stat;
  86. Y,M,D,hh,mm,ss : word;
  87. begin
  88. If fpstat (FileName,Info)<0 then
  89. exit(-1)
  90. else
  91. begin
  92. EpochToLocal(info.st_mtime,y,m,d,hh,mm,ss);
  93. Result:=DateTimeToFileDate(EncodeDate(y,m,d)+EncodeTime(hh,mm,ss,0));
  94. end;
  95. end;
  96. Function FileExists (Const FileName : String) : Boolean;
  97. Var Info : Stat;
  98. begin
  99. FileExists:=fpstat(filename,Info)>=0;
  100. end;
  101. Function DirectoryExists (Const Directory : String) : Boolean;
  102. Var Info : Stat;
  103. begin
  104. DirectoryExists:=(fpstat(Directory,Info)>=0) and fpS_ISDIR(Info.st_mode);
  105. end;
  106. Function LinuxToWinAttr (FN : Pchar; Const Info : Stat) : Longint;
  107. begin
  108. Result:=faArchive;
  109. If fpS_ISDIR(Info.st_mode) then
  110. Result:=Result or faDirectory;
  111. If (FN[0]='.') and (not (FN[1] in [#0,'.'])) then
  112. Result:=Result or faHidden;
  113. If (Info.st_Mode and S_IWUSR)=0 Then
  114. Result:=Result or faReadOnly;
  115. If fpS_ISSOCK(Info.st_mode) or fpS_ISBLK(Info.st_mode) or fpS_ISCHR(Info.st_mode) or fpS_ISFIFO(Info.st_mode) Then
  116. Result:=Result or faSysFile;
  117. end;
  118. {
  119. GlobToSearch takes a glob entry, stats the file.
  120. The glob entry is removed.
  121. If FileAttributes match, the entry is reused
  122. }
  123. Type
  124. TGlobSearchRec = Record
  125. Path : String;
  126. GlobHandle : PGlob;
  127. end;
  128. PGlobSearchRec = ^TGlobSearchRec;
  129. Function GlobToTSearchRec (Var Info : TSearchRec) : Boolean;
  130. Var SInfo : Stat;
  131. p : Pglob;
  132. GlobSearchRec : PGlobSearchrec;
  133. begin
  134. GlobSearchRec:=PGlobSearchrec(Info.FindHandle);
  135. P:=GlobSearchRec^.GlobHandle;
  136. Result:=P<>Nil;
  137. If Result then
  138. begin
  139. GlobSearchRec^.GlobHandle:=P^.Next;
  140. Result:=Fpstat(GlobSearchRec^.Path+StrPas(p^.name),SInfo)>=0;
  141. If Result then
  142. begin
  143. Info.Attr:=LinuxToWinAttr(p^.name,SInfo);
  144. Result:=(Info.ExcludeAttr and Info.Attr)=0;
  145. If Result Then
  146. With Info do
  147. begin
  148. Attr:=Info.Attr;
  149. If P^.Name<>Nil then
  150. Name:=strpas(p^.name);
  151. Time:=Sinfo.st_mtime;
  152. Size:=Sinfo.st_Size;
  153. end;
  154. end;
  155. P^.Next:=Nil;
  156. GlobFree(P);
  157. end;
  158. end;
  159. Function DoFind(Var Rslt : TSearchRec) : Longint;
  160. Var
  161. GlobSearchRec : PGlobSearchRec;
  162. begin
  163. Result:=-1;
  164. GlobSearchRec:=PGlobSearchRec(Rslt.FindHandle);
  165. If (GlobSearchRec^.GlobHandle<>Nil) then
  166. While (GlobSearchRec^.GlobHandle<>Nil) and not (Result=0) do
  167. If GlobToTSearchRec(Rslt) Then Result:=0;
  168. end;
  169. Function FindFirst (Const Path : String; Attr : Longint; Var Rslt : TSearchRec) : Longint;
  170. Var
  171. GlobSearchRec : PGlobSearchRec;
  172. begin
  173. New(GlobSearchRec);
  174. GlobSearchRec^.Path:=ExpandFileName(ExtractFilePath(Path));
  175. GlobSearchRec^.GlobHandle:=Glob(Path);
  176. Rslt.ExcludeAttr:=Not Attr; //!! Not correct !!
  177. Rslt.FindHandle:=Longint(GlobSearchRec);
  178. Result:=DoFind (Rslt);
  179. end;
  180. Function FindNext (Var Rslt : TSearchRec) : Longint;
  181. begin
  182. Result:=DoFind (Rslt);
  183. end;
  184. Procedure FindClose (Var F : TSearchrec);
  185. Var
  186. GlobSearchRec : PGlobSearchRec;
  187. begin
  188. GlobSearchRec:=PGlobSearchRec(F.FindHandle);
  189. GlobFree (GlobSearchRec^.GlobHandle);
  190. Dispose(GlobSearchRec);
  191. end;
  192. Function FileGetDate (Handle : Longint) : Longint;
  193. Var Info : Stat;
  194. begin
  195. If (fpFStat(Handle,Info))<0 then
  196. Result:=-1
  197. else
  198. Result:=Info.st_Mtime;
  199. end;
  200. Function FileSetDate (Handle,Age : Longint) : Longint;
  201. begin
  202. // Impossible under Linux from FileHandle !!
  203. FileSetDate:=-1;
  204. end;
  205. Function FileGetAttr (Const FileName : String) : Longint;
  206. Var Info : Stat;
  207. begin
  208. If FpStat (FileName,Info)<0 then
  209. Result:=-1
  210. Else
  211. Result:=LinuxToWinAttr(Pchar(FileName),Info);
  212. end;
  213. Function FileSetAttr (Const Filename : String; Attr: longint) : Longint;
  214. begin
  215. Result:=-1;
  216. end;
  217. Function DeleteFile (Const FileName : String) : Boolean;
  218. begin
  219. Result:=fpUnLink (FileName)>=0;
  220. end;
  221. Function RenameFile (Const OldName, NewName : String) : Boolean;
  222. begin
  223. RenameFile:=BaseUnix.FpRename(OldNAme,NewName)>=0;
  224. end;
  225. Function FileIsReadOnly(const FileName: String): Boolean;
  226. begin
  227. Result := fpAccess(PChar(FileName),W_OK)= 0;
  228. end;
  229. {****************************************************************************
  230. Disk Functions
  231. ****************************************************************************}
  232. {
  233. The Diskfree and Disksize functions need a file on the specified drive, since this
  234. is required for the statfs system call.
  235. These filenames are set in drivestr[0..26], and have been preset to :
  236. 0 - '.' (default drive - hence current dir is ok.)
  237. 1 - '/fd0/.' (floppy drive 1 - should be adapted to local system )
  238. 2 - '/fd1/.' (floppy drive 2 - should be adapted to local system )
  239. 3 - '/' (C: equivalent of dos is the root partition)
  240. 4..26 (can be set by you're own applications)
  241. ! Use AddDisk() to Add new drives !
  242. They both return -1 when a failure occurs.
  243. }
  244. Const
  245. FixDriveStr : array[0..3] of pchar=(
  246. '.',
  247. '/fd0/.',
  248. '/fd1/.',
  249. '/.'
  250. );
  251. var
  252. Drives : byte;
  253. DriveStr : array[4..26] of pchar;
  254. Procedure AddDisk(const path:string);
  255. begin
  256. if not (DriveStr[Drives]=nil) then
  257. FreeMem(DriveStr[Drives],StrLen(DriveStr[Drives])+1);
  258. GetMem(DriveStr[Drives],length(Path)+1);
  259. StrPCopy(DriveStr[Drives],path);
  260. inc(Drives);
  261. if Drives>26 then
  262. Drives:=4;
  263. end;
  264. Function DiskFree(Drive: Byte): int64;
  265. var
  266. fs : tstatfs;
  267. Begin
  268. if ((Drive<4) and (not (fixdrivestr[Drive]=nil)) and (statfs(StrPas(fixdrivestr[drive]),fs)<>-1)) or
  269. ((not (drivestr[Drive]=nil)) and (statfs(StrPas(drivestr[drive]),fs)<>-1)) then
  270. Diskfree:=int64(fs.bavail)*int64(fs.bsize)
  271. else
  272. Diskfree:=-1;
  273. End;
  274. Function DiskSize(Drive: Byte): int64;
  275. var
  276. fs : tstatfs;
  277. Begin
  278. if ((Drive<4) and (not (fixdrivestr[Drive]=nil)) and (statfs(StrPas(fixdrivestr[drive]),fs)<>-1)) or
  279. ((not (drivestr[Drive]=nil)) and (statfs(StrPas(drivestr[drive]),fs)<>-1)) then
  280. DiskSize:=int64(fs.blocks)*int64(fs.bsize)
  281. else
  282. DiskSize:=-1;
  283. End;
  284. Function GetCurrentDir : String;
  285. begin
  286. GetDir (0,Result);
  287. end;
  288. Function SetCurrentDir (Const NewDir : String) : Boolean;
  289. begin
  290. {$I-}
  291. ChDir(NewDir);
  292. {$I+}
  293. result := (IOResult = 0);
  294. end;
  295. Function CreateDir (Const NewDir : String) : Boolean;
  296. begin
  297. {$I-}
  298. MkDir(NewDir);
  299. {$I+}
  300. result := (IOResult = 0);
  301. end;
  302. Function RemoveDir (Const Dir : String) : Boolean;
  303. begin
  304. {$I-}
  305. RmDir(Dir);
  306. {$I+}
  307. result := (IOResult = 0);
  308. end;
  309. {****************************************************************************
  310. Misc Functions
  311. ****************************************************************************}
  312. procedure Beep;
  313. begin
  314. end;
  315. {****************************************************************************
  316. Locale Functions
  317. ****************************************************************************}
  318. Procedure GetLocalTime(var SystemTime: TSystemTime);
  319. begin
  320. Unix.GetTime(SystemTime.Hour, SystemTime.Minute, SystemTime.Second);
  321. Unix.GetDate(SystemTime.Year, SystemTime.Month, SystemTime.Day);
  322. SystemTime.MilliSecond := 0;
  323. end ;
  324. Procedure InitAnsi;
  325. Var
  326. i : longint;
  327. begin
  328. { Fill table entries 0 to 127 }
  329. for i := 0 to 96 do
  330. UpperCaseTable[i] := chr(i);
  331. for i := 97 to 122 do
  332. UpperCaseTable[i] := chr(i - 32);
  333. for i := 123 to 191 do
  334. UpperCaseTable[i] := chr(i);
  335. Move (CPISO88591UCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
  336. for i := 0 to 64 do
  337. LowerCaseTable[i] := chr(i);
  338. for i := 65 to 90 do
  339. LowerCaseTable[i] := chr(i + 32);
  340. for i := 91 to 191 do
  341. LowerCaseTable[i] := chr(i);
  342. Move (CPISO88591LCT,LowerCaseTable[192],SizeOf(CPISO88591UCT));
  343. end;
  344. Procedure InitInternational;
  345. begin
  346. InitAnsi;
  347. end;
  348. function SysErrorMessage(ErrorCode: Integer): String;
  349. begin
  350. Result:=StrError(ErrorCode);
  351. end;
  352. {****************************************************************************
  353. OS utility functions
  354. ****************************************************************************}
  355. Function GetEnvironmentVariable(Const EnvVar : String) : String;
  356. begin
  357. Result:=StrPas(BaseUnix.FPGetenv(PChar(EnvVar)));
  358. end;
  359. {$define FPC_USE_FPEXEC} // leave the old code under IFDEF for a while.
  360. function ExecuteProcess(Const Path: AnsiString; Const ComLine: AnsiString):integer;
  361. var
  362. pid : longint;
  363. err : longint;
  364. e : EOSError;
  365. CommandLine: AnsiString;
  366. cmdline2 : ppchar;
  367. Begin
  368. { always surround the name of the application by quotes
  369. so that long filenames will always be accepted. But don't
  370. do it if there are already double quotes!
  371. }
  372. {$ifdef FPC_USE_FPEXEC} // Only place we still parse
  373. cmdline2:=nil;
  374. if Comline<>'' Then
  375. begin
  376. CommandLine:=ComLine;
  377. cmdline2:=StringtoPPChar(CommandLine,1);
  378. cmdline2^:=pchar(Path);
  379. end
  380. else
  381. begin
  382. getmem(cmdline2,2*sizeof(pchar));
  383. cmdline2^:=pchar(Path);
  384. cmdline2[1]:=nil;
  385. end;
  386. {$else}
  387. if Pos ('"', Path) = 0 then
  388. CommandLine := '"' + Path + '"'
  389. else
  390. CommandLine := Path;
  391. if ComLine <> '' then
  392. CommandLine := Commandline + ' ' + ComLine;
  393. {$endif}
  394. pid:=fpFork;
  395. if pid=0 then
  396. begin
  397. {The child does the actual exec, and then exits}
  398. {$ifdef FPC_USE_FPEXEC}
  399. fpexecv(pchar(Path),Cmdline2);
  400. {$else}
  401. Execl(CommandLine);
  402. {$endif}
  403. { If the execve fails, we return an exitvalue of 127, to let it be known}
  404. fpExit(127);
  405. end
  406. else
  407. if pid=-1 then {Fork failed}
  408. begin
  409. e:=EOSError.CreateFmt(SExecuteProcessFailed,[CommandLine,-1]);
  410. e.ErrorCode:=-1;
  411. raise e;
  412. end;
  413. { We're in the parent, let's wait. }
  414. result:=WaitProcess(pid); // WaitPid and result-convert
  415. if (result>=0) and (result<>127) then
  416. result:=0
  417. else
  418. begin
  419. e:=EOSError.CreateFmt(SExecuteProcessFailed,[CommandLine,result]);
  420. e.ErrorCode:=result;
  421. raise e;
  422. end;
  423. End;
  424. function ExecuteProcess(Const Path: AnsiString; Const ComLine: Array Of AnsiString):integer;
  425. var
  426. pid : longint;
  427. err : longint;
  428. e : EOSError;
  429. Begin
  430. { always surround the name of the application by quotes
  431. so that long filenames will always be accepted. But don't
  432. do it if there are already double quotes!
  433. }
  434. pid:=fpFork;
  435. if pid=0 then
  436. begin
  437. {The child does the actual exec, and then exits}
  438. fpexecl(Path,Comline);
  439. { If the execve fails, we return an exitvalue of 127, to let it be known}
  440. fpExit(127);
  441. end
  442. else
  443. if pid=-1 then {Fork failed}
  444. begin
  445. e:=EOSError.CreateFmt(SExecuteProcessFailed,[Path,-1]);
  446. e.ErrorCode:=-1;
  447. raise e;
  448. end;
  449. { We're in the parent, let's wait. }
  450. result:=WaitProcess(pid); // WaitPid and result-convert
  451. if (result>=0) and (result<>127) then
  452. result:=0
  453. else
  454. begin
  455. e:=EOSError.CreateFmt(SExecuteProcessFailed,[Path,result]);
  456. e.ErrorCode:=result;
  457. raise e;
  458. end;
  459. End;
  460. procedure Sleep(milliseconds: Cardinal);
  461. Var
  462. fd : Integer;
  463. fds : TfdSet;
  464. timeout : TimeVal;
  465. begin
  466. fd:=FileOpen('/dev/null',fmOpenRead);
  467. If Not(Fd<0) then
  468. begin
  469. fpfd_zero(fds);
  470. fpfd_set(0,fds);
  471. timeout.tv_sec:=Milliseconds div 1000;
  472. timeout.tv_usec:=(Milliseconds mod 1000) * 1000;
  473. fpSelect(1,Nil,Nil,@fds,@timeout);
  474. end;
  475. end;
  476. Function GetLastOSError : Integer;
  477. begin
  478. Result:=fpgetErrNo;
  479. end;
  480. {****************************************************************************
  481. Initialization code
  482. ****************************************************************************}
  483. Initialization
  484. InitExceptions; { Initialize exceptions. OS independent }
  485. InitInternational; { Initialize internationalization settings }
  486. Finalization
  487. DoneExceptions;
  488. end.
  489. {
  490. $Log$
  491. Revision 1.38 2004-04-20 18:24:32 marco
  492. * small fix for NIL arg ptr in first executeprocess
  493. Revision 1.37 2004/03/04 22:15:16 marco
  494. * UnixType changes. Please report problems to me.
  495. Revision 1.36 2004/02/13 10:50:23 marco
  496. * Hopefully last large changes to fpexec and friends.
  497. - naming conventions changes from Michael.
  498. - shell functions get alternative under ifdef.
  499. - arraystring function moves to unixutil
  500. - unixutil now regards quotes in stringtoppchar.
  501. - sysutils/unix get executeprocess(ansi,array of ansi), and
  502. both executeprocess functions are fixed
  503. - Sysutils/win32 get executeprocess(ansi,array of ansi)
  504. Revision 1.35 2004/02/12 15:31:06 marco
  505. * First version of fpexec change. Still under ifdef or silently overloaded
  506. Revision 1.34 2004/02/09 17:11:17 marco
  507. * fixed for 1.0 errno->fpgeterrno
  508. Revision 1.33 2004/02/08 14:50:51 michael
  509. + Added fileIsReadOnly
  510. Revision 1.32 2004/02/08 11:01:17 michael
  511. + Implemented getlastoserror
  512. Revision 1.31 2004/01/20 23:13:53 hajny
  513. * ExecuteProcess fixes, ProcessID and ThreadID added
  514. Revision 1.30 2004/01/10 17:34:36 michael
  515. + Implemented sleep() on Unix.
  516. Revision 1.29 2004/01/05 22:42:35 florian
  517. * compilation error fixed
  518. Revision 1.28 2004/01/05 22:37:15 florian
  519. * changed sysutils.exec to ExecuteProcess
  520. Revision 1.27 2004/01/03 09:09:11 marco
  521. * Unix exec(ansistring)
  522. Revision 1.26 2003/11/26 20:35:14 michael
  523. + Some fixes to have everything compile again
  524. Revision 1.25 2003/11/17 10:05:51 marco
  525. * threads for FreeBSD. Not working tho
  526. Revision 1.24 2003/10/25 23:43:59 hajny
  527. * THandle in sysutils common using System.THandle
  528. Revision 1.23 2003/10/07 08:28:49 marco
  529. * fix from Vincent to casetables
  530. Revision 1.22 2003/09/27 12:51:33 peter
  531. * fpISxxx macros renamed to C compliant fpS_ISxxx
  532. Revision 1.21 2003/09/17 19:07:44 marco
  533. * more fixes for Unix<->unixutil
  534. Revision 1.20 2003/09/17 12:41:31 marco
  535. * Uses more baseunix, less unix now
  536. Revision 1.19 2003/09/14 20:15:01 marco
  537. * Unix reform stage two. Remove all calls from Unix that exist in Baseunix.
  538. Revision 1.18 2003/04/01 15:57:41 peter
  539. * made THandle platform dependent and unique type
  540. Revision 1.17 2003/03/30 10:38:00 armin
  541. * corrected typo in DirectoryExists
  542. Revision 1.16 2003/03/29 18:21:42 hajny
  543. * DirectoryExists declaration changed to that one from fixes branch
  544. Revision 1.15 2003/03/28 19:06:59 peter
  545. * directoryexists added
  546. Revision 1.14 2003/01/03 20:41:04 peter
  547. * FileCreate(string,mode) overload added
  548. Revision 1.13 2002/09/07 16:01:28 peter
  549. * old logs removed and tabs fixed
  550. Revision 1.12 2002/01/25 16:23:03 peter
  551. * merged filesearch() fix
  552. }