Recent

Author Topic: Recursive file search  (Read 365 times)

maurobio

  • Full Member
  • ***
  • Posts: 110
Recursive file search
« on: December 08, 2019, 01:29:03 pm »
Dear ALL,

In my application, I want to check if the R environment for statistical computing and graphics is installed on the computer.

To achieve this, I need to search for the R executable (R on Linux or R.exe on Windows). On Linux, R is usually installed in '/usr/lib/R', but on Windows the situation becomes more complicated (of course!) because each R installation creates a separate subdirectory under 'C:\Program Files\R', based on the installed version number (so, for example, if I have R 3.6.1 installed, the binary executable will be in 'C:\Program Files\R\R-3.6.1\bin'. I cannot know in advance what version of R a user will have in her/his computer, therefore a file serach for R.exe should start at ''C:\Program Files\R' and go down the subdirectories until the binary executable is found.

The FileSearch function in the SysUtils unit seems not to work because as far as I could understand, it does not perform recursive subdirectories searches.

I then attempted to use the FindAllFiles function which is supposed to be in the FileUtil unit (https://wiki.freepascal.org/FindAllFiles):

Code: Pascal  [Select]
  1. program Test;
  2.  
  3. {$MODE OBJFPC}
  4. {$APPTYPE CONSOLE}
  5.  
  6. uses Classes, SysUtils, FileUtil;
  7.  
  8. const
  9. {$ifdef unix}
  10.   FN = 'R';
  11.   P = '/usr/lib/R';
  12. {$else}
  13.   FN = 'R.exe';
  14.   P = 'C:\Program Files\R';
  15. {$endif}
  16.  
  17. var
  18.         APath, AMask: string;
  19.         AList: TStringList;
  20.         i: Integer;
  21.  
  22. begin
  23.         //Writeln ('R is in : ', FileSearch(FN, P));
  24.         AList := TStringList.Create;
  25.         try
  26.                 APath := P;
  27.                 AMask := FN;
  28.                 FindAllFiles(AList, APath, AMask, True, faDirectory);
  29.                 WriteLn('Files found = ', AList.Count);
  30.                 for i := 0 to AList.Count - 1 do
  31.                         WriteLn(AList.Strings[i]);
  32.         finally
  33.                 AList.Free;
  34. end.

But then I got an error message from the compiler, saying that it can't find unit FileUtil. has this unit been removed from FPC/Lazarus?

Anyway, could someone give me some hnelpful insights toward solving the problem of searching recursively for a given file?

Thanks in advance!

Best regards,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.4 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

lucamar

  • Hero Member
  • *****
  • Posts: 2264
Re: Recursive file search
« Reply #1 on: December 08, 2019, 02:44:27 pm »
I have several projects that use it and none has any problem compiling on newest Lazarus 2.0.6. In any case, try adding to your project a dependency on LazUtils package to see if that solves it, although it shouldn't be needed.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.4/2.0.6  - FPC 3.0.4 on:
(K|L)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

maurobio

  • Full Member
  • ***
  • Posts: 110
Re: Recursive file search
« Reply #2 on: December 08, 2019, 02:57:55 pm »
@lucamar,

Thanks, I solved the problem of the unit not found by simply running the test program from within Lazarus (I was previously attempting to compile and execute it directly from the command prompt, as I usually do with other test programs in FPC - old habits never die!).

Anyway, the FindAllFiles routine did not work and just put me into a dreadful infinite loop. I solve the problem with a recursive routine using FindFirst/FindNext (see separate reply). Quite ugly, but at least it works.

Cheers,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.4 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

maurobio

  • Full Member
  • ***
  • Posts: 110
Re: Recursive file search
« Reply #3 on: December 08, 2019, 03:07:31 pm »
Dear ALL,

I solved the problem of searching for the R binary executable under Windows as follows:

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs;
  9.  
  10. const
  11. {$ifdef unix}
  12.   FN = 'R';
  13.   P = '/usr/lib/R';
  14. {$else}
  15.   FN = 'bin\R.exe';
  16.   P = 'C:\Program Files\R';
  17. {$endif}
  18.  
  19. type
  20.  
  21.   { TForm1 }
  22.  
  23.   TForm1 = class(TForm)
  24.     procedure FormCreate(Sender: TObject);
  25.     procedure FormDestroy(Sender: TObject);
  26.   private
  27.  
  28.   public
  29.     lbSearchResult: TStringList;
  30.     procedure FileSearch(const dirName: string);
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39.  
  40. procedure TForm1.FormCreate(Sender: TObject);
  41. var
  42.   I: integer;
  43. begin
  44.   lbSearchResult := TStringList.Create;
  45.   FileSearch(P);
  46.   for I := 0 to lbSearchResult.Count - 1 do
  47.   begin
  48.     if Pos(FN, lbSearchResult[I]) > 0 then
  49.       ShowMessage('R directory: ' + ExtractFilePath(lbSearchResult[I]));
  50.   end;
  51. end;
  52.  
  53. procedure TForm1.FormDestroy(Sender: TObject);
  54. begin
  55.   lbSearchResult.Free;
  56. end;
  57.  
  58. procedure TForm1.FileSearch(const dirName: string);
  59. var
  60.   searchResult: TSearchRec;
  61. begin
  62.   if FindFirst(dirName + '\*', faAnyFile, searchResult) = 0 then
  63.   begin
  64.     try
  65.       repeat
  66.         if (searchResult.Attr and faDirectory) = 0 then
  67.         begin
  68.           if SameText(ExtractFileExt(searchResult.Name), '.exe') then
  69.             lbSearchResult.Append(IncludeTrailingBackSlash(dirName) + searchResult.Name);
  70.         end
  71.         else if (searchResult.Name <> '.') and (searchResult.Name <> '..') then
  72.           FileSearch(IncludeTrailingBackSlash(dirName) + searchResult.Name);
  73.       until FindNext(searchResult) <> 0;
  74.     finally
  75.       FindClose(searchResult);
  76.     end;
  77.   end;
  78. end;
  79.  
  80. end.

Although it works, I found this a very ugly solution, and will appreciate if anyone can provide me with suggestions for improvements.

Cheers,
« Last Edit: December 08, 2019, 03:44:45 pm by maurobio »
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.4 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

jamie

  • Hero Member
  • *****
  • Posts: 2260
Re: Recursive file search
« Reply #4 on: December 08, 2019, 03:32:43 pm »
You should not use hard coded paths for shell directories.

 You should use the "ShGetSpecialFolderLocation"

In your case the CSIDL_PROGRAMS is the key of interest.

Look for examples, they're out there or one of us could throw one at you..
Number 1 at blue screen app creations!

maurobio

  • Full Member
  • ***
  • Posts: 110
Re: Recursive file search
« Reply #5 on: December 08, 2019, 03:41:54 pm »
@jamie,

Thanks, but such register-dependent solutions would not work under Linux. I need a cross-platform solution. In fact, I have already modified my previously post code for providing support to Linux (although also hard-wired).

Cheers,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.4 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

jamie

  • Hero Member
  • *****
  • Posts: 2260
Re: Recursive file search
« Reply #6 on: December 08, 2019, 03:44:03 pm »
Do as you wish with Linux but you should query the shell in windows, which you can do with your existing code, just simply compile in some code that does this when targeting for windows in the same way you have for deciding the path  names now..
Number 1 at blue screen app creations!

maurobio

  • Full Member
  • ***
  • Posts: 110
Re: Recursive file search
« Reply #7 on: December 08, 2019, 03:47:40 pm »
@jamie,

OK, I'll look into it, but the simple idea of diving into the dark depths of the Windows Registry gives me the creeps...  %)

Cheers,
« Last Edit: December 08, 2019, 04:03:35 pm by maurobio »
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.4 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

jamie

  • Hero Member
  • *****
  • Posts: 2260
Re: Recursive file search
« Reply #8 on: December 08, 2019, 03:55:42 pm »
No need for accessing the registry from your code.
The function I showed you will do it for you ,. there are examples..

Later, have a good day..
 :)
Number 1 at blue screen app creations!

lucamar

  • Hero Member
  • *****
  • Posts: 2264
Re: Recursive file search
« Reply #9 on: December 08, 2019, 04:01:39 pm »
I was previously attempting to compile and execute it directly from the command prompt, as I usually do with other test programs in FPC - old habits never die!).

I do the same to (re-)compile small tools and tests but I use lazbuild; it behaves (almost) as if I were compiling in Lazarus itself :)

Quote
Anyway, the FindAllFiles routine did not work and just put me into a dreadful infinite loop.

Strange. It shouldn't do that unless it finds (and resolves) circular links.

I'm attaching a little tool I made to search for files; it's still under development, as you will see for the abundance of notes and TODO comments, but it shows how to use TSearcher (which is what FindAllFiles uses) so it may be useful for you.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.4/2.0.6  - FPC 3.0.4 on:
(K|L)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

maurobio

  • Full Member
  • ***
  • Posts: 110
Re: Recursive file search
« Reply #10 on: December 08, 2019, 04:29:03 pm »
@lucamar,

Thanks, your tool looks great!

Best regards,
UCSD Pascal / Burroughs 6700 / Master Control Program
Lazarus 1.9.3/2.0.4 - FPC 3.0.4 on GNU/Linux Mint 19 ("Tessa"), Windows XP SP3, Windows 7 Professional, Windows 10 Home

PaulRowntree

  • Full Member
  • ***
  • Posts: 107
    • Paul Rowntree
Re: Recursive file search
« Reply #11 on: December 08, 2019, 06:01:01 pm »
Thank you lucamar!
Paul Rowntree
- coding for instrument control, data acquisition & analysis, CNC systems