Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2020-03

MatchesWindowsMask('[x]','[x]') return False

30 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Rolf Wetjen

Hi Lazarus team,

I found this during some error analysis. TMask uses the brackets as a set indicatior in the mask string. In this case the FMask.MinLength and FMask.MaxLength are set to 1. The brackets in the test string are not treated in any special way and so the match fails as the length of the test string is 3 (tested inside TMask.Matches).

I think that this isn't the expected behavior especially as [ and ] are regular character for file and path names in windows.

Shall I open a bug report for this?

BR

Rolf Wetjen

(RolfW)

Diese E-Mail wurde von Avast Antivirus-Software auf Viren geprüft. https://www.avast.com/antivirus

Reply by email

Bart
to Rolf Wetjen
PM Rolf Wetjen wrote
I think that this isn't the expected behavior especially as [ and ] are regular character for file and path names in windows.

MatchesMask doesn;t have an Escape char AFAIK
If you have [[] in the mask, it will match a single [ I guess, but []] isn't going to work for a single ]...

Please file a bugreport.
Somebody should test this behaviour against Delphi.

Bart

Reply by email

Rolf Wetjen
to Bart: On Sun, Mar 15, 2020 at 1:10 PM Rolf Wetjen via lazarus '[[]x]' as a mask will match…

Hi Bart,

sorry, this isn't a workaround if you get the filenames form FindFirst/FindNext and want to test against a fixed mask.

My workaround for MatchesWindowsMask is quick' n'dirty too: Replace [ with #17 and ] with #18 in the mask and in the test string. Anyway, let's wait for the info about Delphi.

Rolf

Diese E-Mail wurde von Avast Antivirus-Software auf Viren geprüft. https://www.avast.com/antivirus

Reply by email

Mattias Gaertner
to Rolf Wetjen: I found this during some error analysis. TMask uses the brackets as a set indicatior in…
Show quoted text 10 lines
Hi Lazarus team, I found this during some error analysis. TMask uses the brackets as a set indicatior in the mask string. In this case the FMask.MinLength and FMask.MaxLength are set to 1. The brackets in the test string are not treated in any special way and so the match fails as the length of the test string is 3 (tested inside TMask.Matches). I think that this isn't the expected behavior especially as [ and ] are regular character for file and path names in windows.

IMO it should check for \[

Mattias

Reply by email

Bart
to Mattias Gaertner
PM Mattias Gaertner wrote
IMO it should check for \[

Using \ as an escape character isn't really a good choice IMO, since this function deals with filenames.
I would be more inclined to have an option to disable sets in the mask.

@Rolf: you can do a StringReplace on the filename, changing '[' into '[[]'. That would do the trick.

Let's wait to hear what Delphi does (got no answer yet).

Bart

Reply by email

Mattias Gaertner
to Bart
Show quoted text 7 lines
On Sun, Mar 15, 2020 at 8:23 PM Mattias Gaertner via lazarus wrote: > IMO it should check for \[ Using \ as an escape character isn't really a good choice IMO, since this function deals with filenames.

So? It is common practice on Unix since decades.

I would be more inclined to have an option to disable sets in the mask.

1. Without backslashing you need sets for ? and * as well.
2. I guess this function exists only for Delphi compatibility. Regular expression are more versatile anyway.

@Rolf: you can do a StringReplace on the filename, changing '[' into '[[]'. That would do the trick. Let's wait to hear what Delphi does (got no answer yet).

I can't find MatchesWindowMask in Delphi 10.3. I found MatchesMask, which works the same. It supports [] too and I can't find any other way than [[].

Mattias

Reply by email

Marco van de Voort
to Mattias Gaertner

Op 2020-03-16 om 00:05 schreef Mattias Gaertner via lazarus:

> Using \ as an escape character isn't really a good choice IMO, since > this function deals with filenames. So? It is common practice on Unix since decades.

Well, euhh, Unix doesn't use backslashes in paths? This would require already existing code suddenly to escape.

Reply by email

Mattias Gaertner
to Marco van de Voort
Show quoted text 7 lines
Op 2020-03-16 om 00:05 schreef Mattias Gaertner via lazarus: >> Using \ as an escape character isn't really a good choice IMO, >> since this function deals with filenames. > So? It is common practice on Unix since decades. Well, euhh, Unix doesn't use backslashes in paths? This would require already existing code suddenly to escape.

True. Since it exists only for Delphi compatibility it is better to not change it.

Mattias

Reply by email

Bart
to Mattias Gaertner: On Sun, 15 Mar 2020 22:19:21 +0100 So? It is common practice on Unix since decades. 1.…
AM Mattias Gaertner wrote
I can't find MatchesWindowMask in Delphi 10.3. I found MatchesMask, which works the same. It supports [] too and I can't find any other way than [[].

I implemented that variation because MatchesMask treats wildcards like *nix does: '*.*' doesn't match 'foo'.
Since this is counterintuitive to Windows users (and most likely not Delphi comapitble) I implemented MatcesWindowsMask. See the implementation to see how Windows handles wildcards different in some (corner) cases.

Bart

Reply by email

Bart
to Mattias Gaertner: On Mon, 16 Mar 2020 00:21:55 +0100 True. Since it exists only for Delphi compatibility it…
AM Mattias Gaertner wrote
True. Since it exists only for Delphi compatibility it is better to not change it.

To be honest, we've already improved our implementation so it can treat the mask case-sensitve (Delphi doesn't have that option). It defaults to False so that MatchesMask(Filename, Mask) is compatible with Delphi.

We could implement a mechanisme to disable sets in the mask. Either by adding yet another boolean parameter to MatchesMask, or by having some sort of Options?

Bart

Reply by email

Bart
to Rolf Wetjen
AM Rolf Wetjen wrote
what's about to implement an additional completly new TMaskReg class without sets but in addition with a regular expression option?

Feel free to implement such a thing.
However, if someone is familiar with RegEx's he/she probably won't need it.

Bart

Reply by email

Rolf Wetjen
to Bart

Hi Lazarus team,

seems that this isn't your favourite option.

Ok, what's about an additional TMask property to control the useage of sets (default should be true for Delphi compatibility) and additional options for the MatchesMask and MatchesWindowsMask functions?

Can I help with a patch in this way?

BTW, where can I get some info how to update the help files?

Rolf

Bart wrote 8 lines
On Tue, Mar 17, 2020 at 8:17 AM Rolf Wetjen via lazarus wrote: > what's about to implement an additional completly new TMaskReg class > without sets but in addition with a regular expression option? Feel free to implement such a thing. However, if someone is familiar with RegEx's he/she probably won't need it.
Diese E-Mail wurde von Avast Antivirus-Software auf Viren geprüft. https://www.avast.com/antivirus

Reply by email

Marco van de Voort
to Rolf Wetjen

Op 19/03/2020 om 08:25 schreef Rolf Wetjen via lazarus:

BTW, where can I get some info how to update the help files?

https://wiki.freepascal.org/How_To_Make_Lazarus_Docs

to rebuild:

Basically (on linux) I run the fixdocs.sh fpc docs repository, and  build_lcl_chm.sh  in lazarus/lcl/docs.

The linux installation requires a texlive and a tex4ht installation iirc.

to change:

submit patch files for the documentation source files *.xml (rtl,lcl,lazutils) or *.tex (prog,user,ref,fpdoc)

There might also be some more details in discussions on the forum.

Trunk (FPC+Lazarus) CHM's from january can be downloaded at http://www.stack.nl/~marcov/doc-chmbeta.zip

Reply by email

John Landmesser
to Rolf Wetjen: seems that this isn't your favourite option. Ok, what's about an additional TMask…
Rolf Wetjen wrote
BTW, where can I get some info how to update the help files?

This one should be enough, on Linux you would do:

New folder, change to new folder and then:

svn co https://svn.freepascal.org/svn/lazarus/binaries/docs/chm/

Then in Lazarus IDE set the path to checkout directory.

The URL should also be enough for tortoise or what you use on windows.

John

Reply by email

Bart
to Rolf Wetjen: seems that this isn't your favourite option. Ok, what's about an additional TMask…
AM Rolf Wetjen wrote
seems that this isn't your favourite option.

Without any context, I cannot comment on this.

Ok, what's about an additional TMask property to control the useage of sets (default should be true for Delphi compatibility) and additional options for the MatchesMask and MatchesWindowsMask functions?

It's on my ToDo list (as long as other devels don't object to this. I intend to have an Options property for that, with for now moCaseSensitive and moDisableSets.
There will be an overloaded constructor TMask,Create((const AValue: String; ASeparator: Char = ';'; const Options: TMaskOptions = []); (By default CaseSensitive and DisableSets must be off for backwards compatibility)

It will only be for trunk and the next stable major release (so it won't go into fixes branch) anyhow.

Bart

Reply by email

Bart
to Bart
PM Bart wrote
It's on my ToDo list (as long as other devels don't object to this. I intend to have an Options property for that, with for now moCaseSensitive and moDisableSets.

Attached is a rough version of the intended patch. The MatchesMaskList() functions needed several overloads to make all previously possible calls (2,3, or 4 parameters) work as before.

Please test.

Bart -------------- next part -------------- A non-text attachment was scrubbed... Name: masks.disablesets.diff Type: application/octet-stream Size: 12495 bytes Desc: not available URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20200319/1372658c/attachment-0001.obj>

Reply by email

Rolf Wetjen
to Bart: On Thu, Mar 19, 2020 at 8:25 AM Rolf Wetjen via lazarus Without any context, I cannot…

Hi Bart,

I did something similar yesterday:

TMask.InitMaskString(const AValue: String; const CaseSensitive: Boolean;
*const UseSets: Boolean = True*);
TMask.Create(const AValue: String; const CaseSensitive: Boolean = False;
*const UseSets: Boolean = True*);
TMaskList.Create(const AValue: String; ASeparator: Char = ';'; const
CaseSensitive: Boolean = False; *const UseSets: Boolean = True*);
function MatchesMask(const FileName, Mask: String; const CaseSensitive:
Boolean = False; *const UseSets: Boolean = True*): Boolean;
function MatchesWindowsMask(const FileName, Mask: String; const
CaseSensitive: Boolean = False; *const UseSets: Boolean = False*): Boolean;
function MatchesMaskList(const FileName, Mask: String; Separator: Char =
';'; const CaseSensitive: Boolean = False; *const UseSets: Boolean =
True*): Boolean;
function MatchesWindowsMaskList(const FileName, Mask: String; Separator:
Char = ';'; const CaseSensitive: Boolean = False; *const UseSets:
Boolean = /False/*): Boolean;
The only code change is in TMask.InitMaskString
-      '[': AddCharSet;
+      '[': if fUseSets then
+             AddCharSet
+           else
+             AddChar;

This works fine.

Easy to implement it as Options.

Regards,

Rolf

Bart wrote 21 lines
On Thu, Mar 19, 2020 at 8:25 AM Rolf Wetjen via lazarus wrote: > seems that this isn't your favourite option. Without any context, I cannot comment on this. > Ok, what's about an additional TMask property to control the useage of > sets (default should be true for Delphi compatibility) and additional > options for the MatchesMask and MatchesWindowsMask functions? It's on my ToDo list (as long as other devels don't object to this. I intend to have an Options property for that, with for now moCaseSensitive and moDisableSets. There will be an overloaded constructor TMask,Create((const AValue: String; ASeparator: Char = ';'; const Options: TMaskOptions = []); (By default CaseSensitive and DisableSets must be off for backwards compatibility) It will only be for trunk and the next stable major release (so it won't go into fixes branch) anyhow.
Diese E-Mail wurde von Avast Antivirus-Software auf Viren geprüft. https://www.avast.com/antivirus -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20200320/491bb47b/attachment.html> -------------- next part -------------- Index: components/lazutils/masks.pas =================================================================== --- components/lazutils/masks.pas (revision 62701) +++ components/lazutils/masks.pas (working copy) @@ -42,11 +42,12 @@ private FMask: TMaskString; fCaseSensitive: Boolean; + fUseSets: Boolean; fInitialMask: String; - procedure InitMaskString(const AValue: String; const CaseSensitive: Boolean); + procedure InitMaskString(const AValue: String; const CaseSensitive: Boolean; const UseSets: Boolean = True); procedure ClearMaskString; public - constructor Create(const AValue: String; const CaseSensitive: Boolean = False); + constructor Create(const AValue: String; const CaseSensitive: Boolean = False; const UseSets: Boolean = True); destructor Destroy; override; function Matches(const AFileName: String): Boolean; @@ -68,7 +69,7 @@ function GetCount: Integer; function GetItem(Index: Integer): TMask; public - constructor Create(const AValue: String; ASeparator: Char = ';'; const CaseSensitive: Boolean = False); + constructor Create(const AValue: String; ASeparator: Char = ';'; const CaseSensitive: Boolean = False; const UseSets: Boolean = True); destructor Destroy; override; function Matches(const AFileName: String): Boolean; @@ -78,10 +79,10 @@ property Items[Index: Integer]: TMask read GetItem; end; -function MatchesMask(const FileName, Mask: String; const CaseSensitive: Boolean = False): Boolean; -function MatchesWindowsMask(const FileName, Mask: String; const CaseSensitive: Boolean = False): Boolean; -function MatchesMaskList(const FileName, Mask: String; Separator: Char = ';'; const CaseSensitive: Boolean = False): Boolean; -function MatchesWindowsMaskList(const FileName, Mask: String; Separator: Char = ';'; const CaseSensitive: Boolean = False): Boolean; +function MatchesMask(const FileName, Mask: String; const CaseSensitive: Boolean = False; const UseSets: Boolean = True): Boolean; +function MatchesWindowsMask(const FileName, Mask: String; const CaseSensitive: Boolean = False; const UseSets: Boolean = False): Boolean; +function MatchesMaskList(const FileName, Mask: String; Separator: Char = ';'; const CaseSensitive: Boolean = False; const UseSets: Boolean = True): Boolean; +function MatchesWindowsMaskList(const FileName, Mask: String; Separator: Char = ';'; const CaseSensitive: Boolean = False; const UseSets: Boolean = False): Boolean; implementation @@ -105,11 +106,11 @@ end; -function MatchesMask(const FileName, Mask: String; const CaseSensitive: Boolean): Boolean; +function MatchesMask(const FileName, Mask: String; const CaseSensitive: Boolean; const UseSets: Boolean = True): Boolean; var AMask: TMask; begin - AMask := TMask.Create(Mask, CaseSensitive); + AMask := TMask.Create(Mask, CaseSensitive, UseSets); try Result := AMask.Matches(FileName); finally @@ -117,11 +118,11 @@ end; end; -function MatchesWindowsMask(const FileName, Mask: String; const CaseSensitive: Boolean): Boolean; +function MatchesWindowsMask(const FileName, Mask: String; const CaseSensitive: Boolean; const UseSets: Boolean = False): Boolean; var AMask: TMask; begin - AMask := TMask.Create(Mask, CaseSensitive); + AMask := TMask.Create(Mask, CaseSensitive, UseSets); try Result := AMask.MatchesWindowsMask(FileName); finally @@ -129,11 +130,11 @@ end; end; -function MatchesMaskList(const FileName, Mask: String; Separator: Char; const CaseSensitive: Boolean): Boolean; +function MatchesMaskList(const FileName, Mask: String; Separator: Char; const CaseSensitive: Boolean; const UseSets: Boolean = True): Boolean; var AMaskList: TMaskList; begin - AMaskList := TMaskList.Create(Mask, Separator, CaseSensitive); + AMaskList := TMaskList.Create(Mask, Separator, CaseSensitive, UseSets); try Result := AMaskList.Matches(FileName); finally @@ -141,11 +142,11 @@ end; end; -function MatchesWindowsMaskList(const FileName, Mask: String; Separator: Char; const CaseSensitive: Boolean): Boolean; +function MatchesWindowsMaskList(const FileName, Mask: String; Separator: Char; const CaseSensitive: Boolean; const UseSets: Boolean = False): Boolean; var AMaskList: TMaskList; begin - AMaskList := TMaskList.Create(Mask, Separator, CaseSensitive); + AMaskList := TMaskList.Create(Mask, Separator, CaseSensitive, UseSets); try Result := AMaskList.MatchesWindowsMask(FileName); finally @@ -155,7 +156,7 @@ { TMask } -procedure TMask.InitMaskString(const AValue: String; const CaseSensitive: Boolean); +procedure TMask.InitMaskString(const AValue: String; const CaseSensitive: Boolean; const UseSets: Boolean = True); var I: Integer; SkipAnyText: Boolean; @@ -293,6 +294,7 @@ begin fCaseSensitive:=CaseSensitive; + fUseSets:=UseSets; SetLength(FMask.Chars, 0); FMask.MinLength := 0; FMask.MaxLength := 0; @@ -304,7 +306,10 @@ case GetCodePoint(AValue,I) of '*': AddAnyText; '?': AddAnyChar; - '[': AddCharSet; + '[': if fUseSets then + AddCharSet + else + AddChar; else AddChar; end; end; @@ -319,12 +324,13 @@ Dispose(FMask.Chars[I].SetValue); end; -constructor TMask.Create(const AValue: String; const CaseSensitive: Boolean); +constructor TMask.Create(const AValue: String; const CaseSensitive: Boolean; const UseSets: Boolean = True); begin fInitialMask := AValue; fCaseSensitive := CaseSensitive; - InitMaskString(AValue, CaseSensitive); + fUseSets:=UseSets; + InitMaskString(AValue, CaseSensitive, UseSets); end; destructor TMask.Destroy; @@ -508,7 +514,7 @@ Result := FMasks.Count; end; -constructor TMaskList.Create(const AValue: String; ASeparator: Char; const CaseSensitive: Boolean); +constructor TMaskList.Create(const AValue: String; ASeparator: Char; const CaseSensitive: Boolean; const UseSets: Boolean = True); var S: TParseStringList; I: Integer; @@ -518,7 +524,7 @@ S := TParseStringList.Create(AValue, ASeparator); try for I := 0 to S.Count - 1 do - FMasks.Add(TMask.Create(S[I], CaseSensitive)); + FMasks.Add(TMask.Create(S[I], CaseSensitive, UseSets)); finally S.Free; end;

Reply by email

Rolf Wetjen
to Bart: Attached is a rough version of the intended patch. The MatchesMaskList() functions needed…

Same change in code as I used for my patch.

So, yes: working.

Rolf

Bart wrote 12 lines
On Thu, Mar 19, 2020 at 6:37 PM Bart wrote: > It's on my ToDo list (as long as other devels don't object to this. > I intend to have an Options property for that, with for now > moCaseSensitive and moDisableSets. Attached is a rough version of the intended patch. The MatchesMaskList() functions needed several overloads to make all previously possible calls (2,3, or 4 parameters) work as before. Please test.
Diese E-Mail wurde von Avast Antivirus-Software auf Viren geprüft. https://www.avast.com/antivirus -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20200320/c4974658/attachment-0001.html>

Reply by email

Bart
to Rolf Wetjen
AM Rolf Wetjen wrote
Same change in code as I used for my patch. So, yes: working.

OK.
I prefer the Options parameter over another Boolean, mainly because you never know if another use-case might raise the need for yet another parameter.

Bart

Reply by email

Bart
to Bart
AM Bart wrote
I prefer the Options parameter over another Boolean, mainly because you never know if another use-case might raise the need for yet another parameter.

Committed in r62782

Bart

Reply by email

Rolf Wetjen
to Bart

Hi Bart,

I'm just looking for an other error in the implementation when sets are used:

Helloworld / He[lo]?orld - False
Helloworld / He[lo]*orld - True
Helloworld / He[lo]world - False

So, it's not the final patch but not that importand as I don't think that sets are used so much.

Regards

Rolf

Diese E-Mail wurde von Avast Antivirus-Software auf Viren geprüft. https://www.avast.com/antivirus

Reply by email

Bart
to Rolf Wetjen
PM Rolf Wetjen wrote
Helloworld / He[lo]?orld - False

The mask only caters for either Hel?world or Heo?world, so that is correct

Helloworld / He[lo]*orld - True

The mask only caters for either Hel*world or Heo*world, so that is correct (the first one matches)

Helloworld / He[lo]world - False

The mask only caters for either Helworld or Heoworld, so that is correct

No bug here.

Note that sets don't work like a RegEx where you can specify e.g. n times either 'l' or 'o'.

Bart

Reply by email

Rolf Wetjen
to Bart: Committed in r62782 Bart

Hi Bart,

I've uploaded a patch for the TMask documentation
(https://bugs.freepascal.org/view.php?id=36798)

Regards

Rolf

Bart wrote 6 lines
On Fri, Mar 20, 2020 at 10:47 AM Bart wrote: > I prefer the Options parameter over another Boolean, mainly because > you never know if another use-case might raise the need for yet > another parameter. Committed in r62782
Diese E-Mail wurde von Avast Antivirus-Software auf Viren geprüft. https://www.avast.com/antivirus

Reply by email