[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Lost] [Patch] Fix für FilenameMatches
Am Dienstag, 11. März 2008 11:45:59 schrieb Andreas Freimuth:
> so sollte jetzt auch ein * am Ende funktionieren
Das war etwas zu optimistisch gedacht. ;-) Aber danke für das Aufspüren des
Fehlers (oder besser gesagt: eines Fehlers von mehreren). Meine neue Version
sollte es jetzt wirklich tun - ich habe es dieses Mal nicht bei der Theorie
belassen, sondern gleich einen ganzen Haufen Testfälle durchprobiert.
Falls jemand noch mehr Fälle vorschlagen oder probieren möchte, kopiere ich
hier das benutzte Testprogramm rein:
{$MODE ObjFPC}
{$R+}
{$S+}
{$H+}
uses crt, sysutils;
procedure DoTest(pattern, filename: String; result: boolean);
var
msg: String;
begin
msg := '';
try
if FilenameMatches(filename, pattern) = result then begin
Write('OK ');
end else begin
Write('FAIL ');
end;
except
on e: Exception do begin
Write('EXCEPT ');
msg := e.message;
end;
end;
Write(result : 6, ' "', filename, '" in "', pattern, '"');
if msg <> '' then begin
TextColor(12);
Write(' ', msg);
TextColor(7);
end;
WriteLn;
end;
begin
DoTest('foo', 'foo', true);
DoTest('foo', 'bar', false);
DoTest('foo', 'foobar', false);
DoTest('foobar', 'foo', false);
DoTest('foo*', 'foo', true);
DoTest('foo*', 'foobar', true);
DoTest('foo*', 'bar', false);
DoTest('*foo', 'foo', true);
DoTest('*foo', 'foobar', false);
DoTest('*foo', 'barfoo', true);
DoTest('*', '', true);
DoTest('*', 'foobar', true);
DoTest('fo*o', 'foo', true);
DoTest('fo*o', 'foto', true);
DoTest('fo*o', 'fox', false);
DoTest('fo*o', 'fotos', false);
DoTest('f*o*o', 'foo', true);
DoTest('f*o*o', 'foto', true);
DoTest('f*o*o', 'ftoo', true);
DoTest('f*o*o', 'ftoxo', true);
DoTest('f*o*o', 'fooo', true);
DoTest('f*o*o', 'footos', false);
DoTest('f*o*o', 'ftos', false);
DoTest('f*o*o', 'fto*', false);
DoTest('f?o', 'foo', true);
DoTest('f?o', 'bar', false);
DoTest('f??o', 'fxyo', true);
DoTest('f?*?*?', 'foo', false);
DoTest('f?*?*?', 'foo!', true);
DoTest('f?*?*?', 'foobar!!!!1111111111', true);
end.
Index: src/modules/pas/lib/dos/dos.pas
===================================================================
--- src/modules/pas/lib/dos/dos.pas (Revision 725)
+++ src/modules/pas/lib/dos/dos.pas (Arbeitskopie)
@@ -144,36 +144,64 @@
subname: String;
j: integer;
begin
- // TODO FilenameMatches
- WriteLn('FilenameMatches: '+filename+' = '+pattern);
i_pattern := 0;
i_file := 1;
+
+ // Zeichenweise das Pattern durchgehen und pruefen, ob das Zeichen
+ // uebereinstimmt
while i_pattern < length(pattern) do begin
Inc(i_pattern);
+ // Falls wir mit dem Dateinamen schon am Ende sind, duerfen im
+ // Pattern ab jetzt nur noch beliebig viele * kommen
+ if i_file > length(filename) then begin
+ while (i_pattern <= length(pattern))
+ and (pattern[i_pattern] = '*') do
+ begin
+ Inc(i_pattern);
+ end;
+ exit(i_pattern = length(pattern) + 1);
+ end;
+
case pattern[i_pattern] of
'?':
- begin
+ // Ein ? ist leicht zu parsen: Passt, naechstes Zeichen
+ begin
Inc(i_file);
end;
'*':
+ // Ein * dagegen ist etwas schwieriger zu pruefen. Der Trick
+ // besteht darin, auszuprobieren, ob der Rest passen wuerde,
+ // wenn der * das naechste, die naechsten zwei, usw. Zeichen
+ // des Dateinamens abdeckt. Die Pruefung, ob der Rest passt,
+ // wird durch rekursiven Aufruf von FilenameMatches erledigt.
begin
- while pattern[i_pattern] = '*' do begin
+ // Wenn mehrere * aufeinander folgen, koennen bis auf
+ // einen alle ignoriert werden
+ while (i_pattern <= length(pattern))
+ and (pattern[i_pattern] = '*') do
+ begin
Inc(i_pattern);
end;
-
+
+ // Jede moegliche Ersetzung fuer das * durchprobieren,
+ // angefangen beim leeren String bis zur Ersetzung des
+ // kompletten restlichen Dateinamens
pattern := Copy(pattern, i_pattern, length(pattern));
- for j := i_file to length(filename) do begin
+ for j := length(filename) + 1 downto i_file do begin
subname := Copy(filename, j, length(filename));
if FilenameMatches(subname, pattern) then begin
exit(true);
end;
end;
+
exit(false);
end;
else
+ // Ansonsten sind wir im trivialen Fall: Das Zeichen in
+ // Dateiname und Pattern muss uebereinstimmen
begin
if pattern[i_pattern] <> filename[i_file] then begin
exit(false);
@@ -181,22 +209,14 @@
Inc(i_file);
end;
end;
-
- if i_file > length(filename) then begin
- exit(false);
- end;
end;
+
+ // Wenn wir hier landen, ist das Pattern abgearbeitet. An dieser Stelle
+ // muss dann auch der Dateiname zu Ende sein.
+ exit(i_file = length(filename) + 1);
+end;
- exit(i_file = length(filename));
-{
- if pattern = '*' then begin
- exit(true);
- end;
- exit (filename = pattern);
-}
-end;
-
Procedure FindFirst (const Path: pathstr; Attr: word; var F: SearchRec);
var
dir, name, ext: String;