Hi again,

Previous patch still missed to correct some bugs (those we're still bugs in original FNMatch code, not new bugs introduced by my fixes, in case you might ask...).

  '*o' should not match 'blah.ow' and
  '*o' should not match 'fox'

Anyway, I'm attaching new version of the patch (unixutil.patch) and new version of the test program (fnmatch_test.pas). Just forget about previous ones.

BTW, I also corrected indentation around that couple lines of code that I am fixing. I hope you don't mind. Original indentation was totally messed up and inconsistent; I just couldn't resist, so I corrected it.
I hope this will make reading this code easier.


--
Michalis
{ Some tests of UnixUtil.FNMatch function
  (or Linux.FNMatch function under FPC 1.0.10). }
  
uses 
  SysUtils, {$ifdef VER1_0} Linux {$else} UnixUtil {$endif}, Libc;
  
{ Tests results of UnixUtil.FNMatch versus given "GoodResult"
  and result of Libc.FNMatch. They all should be equal. 
  (I'm using Libc.FNMatch to
    1) make sure that GoodResult is really correct
    2) show that UnixUtil.FNMatch behaves really the same way as
       FNMatch from Libc (actually, Libc.FNMatch should get 
       FNM_NOESCAPE as the 3rd arg to be fully identical)
       (i.e., it behaves the same after applying my fixes to 
       UnixUtil.FNMatch...)
    3) test Libc, while we're at it :-)
  )
}
procedure Check(const Pattern, Name:string; GoodResult:boolean);
var UnixUtilResult, LibcResult:boolean;
begin
 UnixUtilResult:= 
   {$ifdef VER1_0} Linux {$else} UnixUtil {$endif} .FNMatch(Pattern, Name);
 LibcResult:=Libc.FNMatch(PChar(Pattern), PChar(Name), 0) = 0;

 { We have 3 results. All should be equal. }
 if (UnixUtilResult<>LibcResult) or (LibcResult<>GoodResult) then
  Writeln(Format('"%s" with "%s" incorrect: %6s %6s %6s',
    [ Pattern, Name,
      BoolToStr(UnixUtilResult), BoolToStr(LibcResult), BoolToStr(GoodResult) ]));
end;

begin
 { Those tests fail with original FNMatch code, because FNMatch 
   allowed '*x' (for any 'x') to match anything (ending with 'x' or not, 
   zero length or not). }
 Check('*~', 'foo', false);
 Check('*b', 'foo', false);
 Check('*?', '', false);
 Check('???*o', 'foo', false);
 Check('*???*o', 'foo', false);

 (*This test fails with original FNMatch code, because after line
     'inc(j);{We didn't find one, need to look further}'
   found is still assumed to be true (while it should be false) *)
 Check('*o', 'blah.ow', false);
 
 { This fails with original FNMatch code because subsequent tries
   to match char right after '*' (i.e., 'o' in this case) actually
   can miss that 'x' <> 'o' when 'x' is the last char of Name. }
 Check('*o', 'fox', false);

 { When first error is solved, we can see other problem
   (that was hidden by previous bug):
   When the '?' in Pattern matches last char of Name,
   some problems arise. That's because of original FNMatch code
     '?' : begin
             inc(j);
             Found:=(j<=LenName);
           end;
   Nonsense ?
   This should check FIRST if (j<=LenName). 
   If not, if should terminate whole DoFNMatch with false, 
   not only the loop labeled
   'find the next character in pattern, different of ? and *'.
   And in that loop, variable i should get a chance to be > LenPat. 
   
   Tests below ensure that these additional fixes are also applied. 
   I.e. these tests worked before my fixes were applied AND they
   work after my fixes are applied. But they we're causing trouble
   when I was working on this and my fixes we're applied only partially. }
 Check('*?', '?', true);
 Check('*?', 'a', true);
 
 { Some additional tests, they worked before my fix and they work
   after my fix. Just to be sure that everything is OK now. }
 Check('*o', 'foo', true);
 Check('*.~', 'foo', false);
 Check('*.b', 'foo', false);
 Check('*.o', 'foo', false);
 Check('*??*o', 'foo', true);
 Check('?o', 'foo', false);
 Check('??o', 'foo', true);
 Check('?o?', 'foo', true);
 Check('o??', 'foo', false);
 Check('*', 'foo', true);
end.
Index: unixutil.pp
===================================================================
RCS file: /FPC/CVS/fpc/rtl/unix/unixutil.pp,v
retrieving revision 1.5
diff -u -u -r1.5 unixutil.pp
--- unixutil.pp 15 Mar 2004 20:43:07 -0000      1.5
+++ unixutil.pp 6 May 2004 05:39:55 -0000
@@ -227,44 +227,56 @@
       '?' : Found:=(j<=LenName);
       '*' : Begin
             {find the next character in pattern, different of ? and *}
-              while Found and (i<LenPat) do
+              while Found do
                 begin
                 inc(i);
+                if i>LenPat then Break;
                 case Pattern[i] of
                   '*' : ;
                   '?' : begin
+                          if j>LenName then begin DoFNMatch:=false; Exit; end;
                           inc(j);
-                          Found:=(j<=LenName);
                         end;
                 else
                   Found:=false;
                 end;
                end;
+              Assert((i>LenPat) or ( (Pattern[i]<>'*') and (Pattern[i]<>'?') ));
             {Now, find in name the character which i points to, if the * or ?
              wasn't the last character in the pattern, else, use up all the
              chars in name}
-              Found:=true;
+              Found:=false;
               if (i<=LenPat) then
-                begin
+              begin
                 repeat
-                {find a letter (not only first !) which maches pattern[i]}
-                while (j<=LenName) and (name[j]<>pattern[i]) do
-                  inc (j);
-                 if (j<LenName) then
+                  {find a letter (not only first !) which maches pattern[i]}
+                  while (j<=LenName) and (name[j]<>pattern[i]) do
+                    inc (j);
+                  if (j<LenName) then
                   begin
                     if DoFnMatch(i+1,j+1) then
-                     begin
-                       i:=LenPat;
-                       j:=LenName;{we can stop}
-                       Found:=true;
-                     end
-                    else
-                     inc(j);{We didn't find one, need to look further}
+                    begin
+                      i:=LenPat;
+                      j:=LenName;{we can stop}
+                      Found:=true;
+                      Break;
+                    end else
+                      inc(j);{We didn't find one, need to look further}
+                  end else
+                  if j=LenName then
+                  begin
+                    Found:=true;
+                    Break;
                   end;
-               until (j>=LenName);
-                end
-              else
+                  { This 'until' condition must be j>LenName, not j>=LenName.
+                    That's because when we 'need to look further' and
+                    j = LenName then loop must not terminate. }
+                until (j>LenName);
+              end else
+              begin
                 j:=LenName;{we can stop}
+                Found:=true;
+              end;
             end;
      else {not a wildcard character in pattern}
        Found:=(j<=LenName) and (pattern[i]=name[j]);

Reply via email to