[Back to STRINGS SWAG index]  [Back to Main SWAG index]  [Original]

Unit USPat; {String pattern a-la Messy-DOS}
{ (C) 1994 William Arthur Barath.   Permission granted for free use in
  Commercial and Non-Commercial software. }

{ written oct 17/94 for TOMMY by WSEM at the request of Weird Al}
{ For use in UFO's text/file scanner.  Fast enough? }

Interface

Type pString = ^String;
Var SpatStr:pString;

Procedure UpCaseStr(Var s:String);
{call to convert a VAR ARG string to upper case.  Don't use w/ PCHAR!}
Procedure SetSPat(Var s:String);
{call to set the pattern to test against with each following call to
 Spat.  This sets a global pointer to the given string and converts that
 string to a format that can be read optimally fast, which saves passing
 the pattern arguement to the SPat PROC via the stack, which saves many
 many clock cycles and memory R\W accesses. 'S' *must* be a string of at
 least 12 characters, or a typecast region of memory of at least 13 bytes
 formatted as a Pascal-style STRING or ugly things may happen.}
Function SPat(Var s:String):Boolean;
{tests the given VAR ARG string against the string pattern pointed to by
 the Public SpatStr global pointer.  Passing a VAR ARG takes much less
 time since only a 4-byte pointer is pushed onto the stack prior to calling
 this PROC, as opposed to a full STRING, which may be 256 bytes and would 
be
 pushed a single char at a time... yawn...}
Function UCSPat(Var s:String):Boolean;
{tests the given VAR ARG string against the string pattern pointed to by
 the Public SpatStr global pointer.  Passing a VAR ARG takes much less
 time since only a 4-byte pointer is pushed onto the stack prior to calling
 this PROC, as opposed to a full STRING, which may be 256 bytes and would 
be
 pushed a single char at a time... yawn... Works with UPCASE'd data}

Implementation

Procedure UpCaseStr(Var s:String);assembler;
{up to 15 times faster than Borland's ASM demo code}
asm Push ds;Lds si,s;Xor ch,ch;Lodsb;Mov cl,al;Jcxz @Done;Mov dx,'az';
Mov ah,'a'-'A';Mov bx,-1;@Loop: Lodsb;Cmp al,dh;Jb @Upper;Cmp al,dl;
ja @Upper;Sub al,ah;Mov [si+bx],al;@Upper: Loop @Loop;@Done: Pop ds;end;

Procedure SetSPat(Var s:String);
{I'd write this in ASM as well, but it isn't likely to enter a loop so
 speed isn't really critical, and it may be useful to edit this to alter
 the personality of the pattern matching algorhythm.}
Type str12 = String[12];
Var l,p:Word;pat:Str12;
Begin
  If s[0]=#0 then s:='*.*';
   UpCaseStr(s);p:=1;
   For l:=1 to 12 do Case s[p] of
     '*':If l=9 then Begin Dec(l);Inc(p);end else pat[l]:='?';
     '.':If l=9 then Begin pat[l]:='.';Inc (p);end else pat[l]:=' ';
     Else Begin pat[l]:=s[p];If Char(p)<s[0] then Inc(p);end;
   end;
  Pat[0]:=Char(l);
  s:=pat;SPatStr:=@s;
end;

Function SPat(Var s:String):Boolean;assembler;
asm
  Push ds           {do this or die... :-) }
    Lds si,SpatStr  {location of the pattern string}
    Les di,s        {location of the test string}
    Lodsb
    Mov cl,es:[di]  {length of the test string}
    xor ch,ch
    Jcxz @BadMatch  {if the test string is NULL then never match}
    Inc di
@Search:
    Mov ah,es:[di]
    Cmp ah,'a'
    Jb  @Search2
    Cmp ah,'z'
    Ja  @Search2
    Sub ah,'a'-'A'  {convert the test string char to CAPS}
@Search2:
    Lodsb           {read and advance a char in pattern}
    Cmp ah,al
    Jz  @Match2     {if the characters are = }
    Cmp al,'?'
    Jnz @BadMatch   {pattern didn't match}
@Match:
    Cmp ah,'.'      {if '?' tries to match a dot, we try the next}
    jz  @search2    {char, which should be either '.' or '?'}
@Match2:
    Inc di          {advance to the next test string char}
    Loop @Search    {test for # of chars in test string}
    Mov al,True
    Jnz @Done       {return 'True'}
@BadMatch:
    xor ax,ax       {return 'False'}
@Done:
  Pop ds            {do this or die... :-) }
end;
Function UCSPat(Var s:String):Boolean;assembler;
asm
  Push ds           {do this or die... :-) }
    Lds si,SpatStr  {location of the pattern string}
    Les di,s        {location of the test string}
    Mov cl,[di]     {length of the test string}
    xor ch,ch
    Jcxz @Bad       {if the test string is NULL then never match}
    Inc cx          {use length+1, so when we hit 0 we know we're done}
    CMPSB           {sneaky way to INC DI and INC SI with one byte :-) }
    Mov dx,'?.'
    Mov bx,-1       {offset to last character.  faster than using immed. 
data}
@Search:
    REPZ CMPSB      {compare bytes until one doesn't match or CX = 0}
    Jcxz @Good      {when we hit 0, we're done.  Last comparison was 
garbage}
    cmp dh,[si+bx]  {If last pattern byte <> '?' then match is bad}
    Jnz @Bad
    cmp dl,[di+bx]  {If last test byte <> '.' then check next chars}
    Jnz @Search
    Dec di          {otherwise, make sure remaining pattern chars}
    Inc cx          {are '?'.  Otherwise, pattern should fail}
    Jmp @Search
@Good:
    Inc ch          {change the exit condition in ch from 0 to 1}
@Bad:
    Mov al,ch
  Pop ds            {do this or die... :-) }
end;
end.



[Back to STRINGS SWAG index]  [Back to Main SWAG index]  [Original]