Recent

Author Topic: How to validate ParamStr(1) string as a good path  (Read 8676 times)

lgrfbs

  • Jr. Member
  • **
  • Posts: 77
    • My CV page (On Swedish only)
How to validate ParamStr(1) string as a good path
« on: December 29, 2024, 09:46:23 am »
How do I check that the text string coming from ParamStr(1) is a valid path in Windows and Lazarus 3.6?
OS : Win 10 64bit Home * Win 7 64bit Professional
Lazarus 1.8.4 r57972 FPC 3.0.4 i386-win32-win32/win64
Delphi 7.0 (Build 4.453)

cdbc

  • Hero Member
  • *****
  • Posts: 2104
    • http://www.cdbc.dk
Re: How to validate ParamStr(1) string as a good path
« Reply #1 on: December 29, 2024, 10:05:15 am »
Hi
Hmmm, I would pick apart the paramstr(x), in its separate directories, perhaps with 'Split' on 'PathSeparator' and then start building the path-string dir by dir and along the way check the path-string with 'DirExists' until you have the final input string.
If some dirs doesn't exist, just 'MkDir' it as you go along...
If you only need to make sure, that the directory path exist, then just use 'ForceDirectories', this function will take care of it all.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

lgrfbs

  • Jr. Member
  • **
  • Posts: 77
    • My CV page (On Swedish only)
Re: How to validate ParamStr(1) string as a good path
« Reply #2 on: December 29, 2024, 10:39:21 am »
Thank you for your reply.
ParamStr(1) should only contain a path specified by the user to a file that is not created yet, but since users can try to specify information other than a path, I want to check that it is a real path.
This is my test so far:
Code: Pascal  [Select][+][-]
  1.   if ParamCount>1 then
  2.   Begin
  3.     //https://forum.lazarus.freepascal.org/index.php?topic=15715.0
  4.     LE_ProgPath.Text:=ParamStr(1); //Put the path string from user int to LE_ProgPath.Text.
  5.  
  6.     //Did the user end path with '\' or not?
  7.     if Copy(LE_ProgPath.Text,Length(LE_ProgPath.Text),1)='\' Then
  8.     Begin
  9.       //The check did find '\'
  10.       LE_ProgPath.Text:=LE_ProgPath.Text+'NC_clean.ini';
  11.     End
  12.     Else
  13.     Begin
  14.       // The chek did not find '\', we will add it.
  15.       LE_ProgPath.Text:=LE_ProgPath.Text+'\C_clean.ini';
  16.     End;
  17.  
  18.     // Is the user path string bigger the 0 in Length?
  19.     if Length(ParamStr(1))>0 then
  20.     Begin
  21.       //IS this string a true path?
  22.      Label2.Caption:=ParamStr(1);
  23.      Label3.Caption:='Fale';
  24.       if DirectoryExists(ParamStr(1)) then
  25.       Begin
  26.        Label3.Caption:='True';
  27.       End;
  28.       //Check if the CheckBox 'Deactivete global fontsize controller' named 'CB_GlobalFontSize'
  29.       if CB_GlobalFontSize.Checked=True then
  30.       Begin
  31.         //Deactivate the SpinEdit and ScrollBar
  32.         Form1.SE_DirectFontSize.Visible:=False;
  33.         Form1.SB_SetFontSize.Visible:=False;
  34.       End;
  35.       GB_BeforeNC.Visible:=True;
  36.       GB_AfterNC.Visible:=True;
  37.       GB_PreSelected.Visible:=True;
  38.       GB_FontsBox.Visible:=True;
  39.     End;
  40.   End;
  41.  
OS : Win 10 64bit Home * Win 7 64bit Professional
Lazarus 1.8.4 r57972 FPC 3.0.4 i386-win32-win32/win64
Delphi 7.0 (Build 4.453)

Bart

  • Hero Member
  • *****
  • Posts: 5558
    • Bart en Mariska's Webstek
Re: How to validate ParamStr(1) string as a good path
« Reply #3 on: December 29, 2024, 12:11:16 pm »
Your logic of adding a trailing backslash has a potential problem.
If ParamStr(1) is 'C:', this is a valid path, but it means "the current active directory on drive "C".
Your code will transfrom that int 'C:\NC_clean.ini', which is definitely not what you want.

That aside: your code does exactly what IncludeTrailingPathDelimiter() does.
You can rewrite  that part as:
Code: Pascal  [Select][+][-]
  1.   LE_ProgPath.Text:=IncludeTrailingPathDelimiter(LE_ProgPath.Text)+'NC_clean.ini';

Or, if you want to handle the 'C:' case as well:
Code: Pascal  [Select][+][-]
  1.   if Length(LE_ProgPath.Text > 1) and (LE_ProgPath.Text[Length(LE_ProgPath.Text)] = ':') then
  2.     LE_ProgPath.Text := LE_ProgPath.Text + 'NC_clean.ini'
  3.   else
  4.   LE_ProgPath.Text:=IncludeTrailingPathDelimiter(LE_ProgPath.Text)+'NC_clean.ini';


What exactly do yo mean by "I want to check that it is a real path"?
- A path that actually exists? --> See: DirectoryExists() function
- A string that Windows would accept as being a path? --> a bit tricky

Bart

lgrfbs

  • Jr. Member
  • **
  • Posts: 77
    • My CV page (On Swedish only)
Re: How to validate ParamStr(1) string as a good path
« Reply #4 on: December 29, 2024, 02:35:23 pm »
Thank you for being almost mind readers and asking questions about what I failed to include in my posts. Thank you all.
Bart, I have tested your code now and had to adjust it a bit as
some ()- characters were missing.
Code: Pascal  [Select][+][-]
  1.     if (Length(LE_ProgPath.Text) > 1) and (LE_ProgPath.Text[Length(LE_ProgPath.Text)] = ':') then
  2.       LE_ProgPath.Text := LE_ProgPath.Text + '\NC_clean.ini'
  3.     else
  4.     LE_ProgPath.Text:=IncludeTrailingPathDelimiter(LE_ProgPath.Text)+'NC_clean.ini';
  5.  
This code works as I want/expect.



Quote
What exactly do yo mean by "I want to check that it is a real path"?

- A path that actually exists? --> See: DirectoryExists() function
- A string that Windows would accept as being a path? --> a bit tricky
The option: A path that actually exists? --> See: DirectoryExists() function

I will read up on DirectoryExists

I've done some testing and on my Windows 10 system C:\ is read-only in the CMD window so my program probably can't put the NC_clean.ini file there even after checking that it's a real path.

Typing C: in the cmd window just takes you directly to the drive and folder you were in when you left the drive or opened the CMD window, but for example if I want to see the files in drive C and the root I type DIR C:\ and everything in the root is listed on the screen.
If I want to look in a file in the C drive root, when I am not in that folder, for example: type C:\App.txt this will cause the contents of the file to be listed on the screen.

The idea is thus that:
A; Check that the text the user entered as a path is really a path that exists on the system and discard unknown commands/nonsense text from the user.
B; Once the user wants to save their settings in the program to the NC_clean.ini file, the location should be writable.
OS : Win 10 64bit Home * Win 7 64bit Professional
Lazarus 1.8.4 r57972 FPC 3.0.4 i386-win32-win32/win64
Delphi 7.0 (Build 4.453)

Bart

  • Hero Member
  • *****
  • Posts: 5558
    • Bart en Mariska's Webstek
Re: How to validate ParamStr(1) string as a good path
« Reply #5 on: December 29, 2024, 04:01:21 pm »
Typing C: in the cmd window just takes you directly to the drive and folder you were in when you left the drive or opened the CMD window

If you have more than 1 drive, e.g. C: and D: then you can do something like this:
Code: [Select]
C:\>cd users\bart
C:\Users\Bart>d:
D:\>cd Boot
D:\Boot>c:
C:\Users\Bart>

Now notice that typing "C:" at the prompt brings me back to "C:\Users\Bart" and not to "C:\".
 
The idea is thus that:
A; Check that the text the user entered as a path is really a path that exists on the system and discard unknown commands/nonsense text from the user.
That's a bit tricky.
You can check for "forbidden characters" , at least '<', '>', '|', '*' and '?' are not allowed on Windows.
But, there's an exception to that as well.
If a path starts with '\\?\' then everythig after is is treated as a literal character.
(You can create folders/files that way, which you cannot delete in Windows Explorer  :D )
Also, some filenames are forbidden (like: 'CON', 'PRN').
See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file for more information on what is allowed and what not.

B; Once the user wants to save their settings in the program to the NC_clean.ini file, the location should be writable.
Have a look at DirectoryIsWritable() in LazFileUtils unit.

If you allow the user to specify a directory that does not exist yet, you could try to use ForceDirectoriesUtf8.
It will create the folder if Windows allows it and then return True, it will fail and return False if it cannot create the folder.
(If the folder already exists, it will return True as well.)
Code: Pascal  [Select][+][-]
  1.   if not ForceDirectoriesUtf8('C:\Users\Bart\Foo\Bar\MySettings') then
  2.     ShowMessage('Unable to create folder');

Bart

jcmontherock

  • Sr. Member
  • ****
  • Posts: 290
Re: How to validate ParamStr(1) string as a good path
« Reply #6 on: December 29, 2024, 04:47:06 pm »
Don't forget also:  FileExists or FileExistsUTF8 functions if file or directory exists.
« Last Edit: December 29, 2024, 05:22:56 pm by jcmontherock »
Windows 11 UTF8-64 - Lazarus 4.0RC2-64 - FPC 3.2.2

lgrfbs

  • Jr. Member
  • **
  • Posts: 77
    • My CV page (On Swedish only)
Re: How to validate ParamStr(1) string as a good path
« Reply #7 on: December 31, 2024, 05:01:12 pm »
I want to thank you for all the answers I have received so far, so THANK YOU.
I will get back to you after 2025 has landed, so Happy New Year to the whole forum.
OS : Win 10 64bit Home * Win 7 64bit Professional
Lazarus 1.8.4 r57972 FPC 3.0.4 i386-win32-win32/win64
Delphi 7.0 (Build 4.453)

silvercoder70

  • Full Member
  • ***
  • Posts: 165
    • Tim Coates
Re: How to validate ParamStr(1) string as a good path
« Reply #8 on: January 01, 2025, 01:34:47 am »
Quick question...

Does the path have to be absolute or can it be relative?

Code: Pascal  [Select][+][-]
  1. function IsFileOrDirectory(const Path: string): Boolean;
  2. var
  3.   SearchRec: TSearchRec;
  4. begin
  5.   Result := False;
  6.   // Check if it's a file or directory using FindFirst
  7.   if FindFirst(Path, faAnyFile, SearchRec) = 0 then
  8.   begin
  9.     // Check if it's a directory
  10.     if (SearchRec.Attr and faDirectory) <> 0 then
  11.       Result := True  // Directory found
  12.     // Check if it's a regular file
  13.     else
  14.       Result := True; // File found
  15.     FindClose(SearchRec);
  16.   end;
  17. end;

Depending on your needs you can add code around that. :)

Explore the beauty of modern Pascal programming with Delphi & Free Pascal - https://www.youtube.com/@silvercoder70

n7800

  • Sr. Member
  • ****
  • Posts: 284
Re: How to validate ParamStr(1) string as a good path
« Reply #9 on: January 01, 2025, 01:23:12 pm »
You can check for "forbidden characters" , at least '<', '>', '|', '*' and '?' are not allowed on Windows.
But, there's an exception to that as well.
If a path starts with '\\?\' then everythig after is is treated as a literal character.
(You can create folders/files that way, which you cannot delete in Windows Explorer  :D )
Also, some filenames are forbidden (like: 'CON', 'PRN').
See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file for more information on what is allowed and what not.

I also wanted to give this link along with this one, which lets many programmers understand that they know nothing about Windows ))

But I can't resist making things even more complicated:
  • Path length limitation. For example, paths longer than 260 characters cannot be valid, even if they exist! However, UNC paths can (though they also have a length limitation). And starting with Windows 10, there are changes.
  • WinAPI functions can automatically convert "/" to "\" (and Lazarus/FPC also partially supports this due to cross-platform). So paths through "/" can be valid (or they may not).
  • NTFS file streams ("C:\filename:streamname").
  • 8.3 short names ("C:\PROGRA~1").
  • Symbolic links, junctions, hard links.
  • ...



But seriously, programs shouldn't check all of this. If the first argument is supposed to take a path, the program should assume it's a path. Even if you check all of the above, you may still get an error reading/writing the file (even just because of low disk space or permissions). Utilities should have flags to specify a specific type of parameter, like "-f <path>" for a file and "-m <msg>" for some text (or one of the flags can be assumed by default).

Bart

  • Hero Member
  • *****
  • Posts: 5558
    • Bart en Mariska's Webstek
Re: How to validate ParamStr(1) string as a good path
« Reply #10 on: January 01, 2025, 11:49:44 pm »
But seriously, programs shouldn't check all of this. If the first argument is supposed to take a path, the program should assume it's a path. Even if you check all of the above, you may still get an error reading/writing the file (even just because of low disk space or permissions). Utilities should have flags to specify a specific type of parameter, like "-f <path>" for a file and "-m <msg>" for some text (or one of the flags can be assumed by default).
Or, if simple commandline program, display the correct syntax upon error (or on suitable commandline parameter like -h, --help /? /h).

Bart

Zvoni

  • Hero Member
  • *****
  • Posts: 2961
Re: How to validate ParamStr(1) string as a good path
« Reply #11 on: January 02, 2025, 08:16:43 am »
His target is Windows, so i'm not going to look for cross-platform.

One of my alltime favorites: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shcreatedirectoryexw
The return-value of it tells you exactly what's going on
No idea which unit to include (windows, jwawindows, no idea)
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

lgrfbs

  • Jr. Member
  • **
  • Posts: 77
    • My CV page (On Swedish only)
Re: How to validate ParamStr(1) string as a good path
« Reply #12 on: January 12, 2025, 06:59:32 am »
I'm sorry it took so long.
The reason for my question is that I wanted to be able to save away settings in the program I am creating.
One solution would be a button [Save Settings] and the code in that button just dumps all the settings in the program's own folder.
However, if the program starts from another folder, the settings file will not be in the “right” place and the program will not load the settings, there by loading the program with the path as a parameter.


It could clearly be that I have overthought everything here and just take the parameter and put it in front of the file name and take care of all errors with a dialog box, like: “Something is wrong with the path you specified [Ok]”

Another reason why the path is given to the program via parameter is that I want to have the possibility to start the program with the “Start with...” menu item in the Windows file manager.

Now I just have to find some inspiration and write some code too...
OS : Win 10 64bit Home * Win 7 64bit Professional
Lazarus 1.8.4 r57972 FPC 3.0.4 i386-win32-win32/win64
Delphi 7.0 (Build 4.453)

Thaddy

  • Hero Member
  • *****
  • Posts: 16813
  • Ceterum censeo Trump esse delendam
Re: How to validate ParamStr(1) string as a good path
« Reply #13 on: January 12, 2025, 07:51:13 am »
No idea which unit to include (windows, jwawindows, no idea)
Shlobj and ShellApi, but jwaWindows is a safe choice because that will pull in a correct file anyway.
If I can't find a Windows unit straight away that is my goto trick.
Changing servers. thaddy.com may be temporary unreachable but restored when the domain name transfer is done.

n7800

  • Sr. Member
  • ****
  • Posts: 284
Re: How to validate ParamStr(1) string as a good path
« Reply #14 on: January 12, 2025, 10:50:25 am »
Yes, the JwaWindows description says that it is more complete and allows you to not include many separate units for working with OS Windows.

 

TinyPortal © 2005-2018