home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 March / Chip_2002-03_cd1.bin / zkuste / delphi / kompon / d5 / cak / CAKINST.ZIP / Masks.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2001-12-12  |  16.6 KB  |  534 lines

  1. {***************************************************************
  2.  *
  3.  * Unit Name : Masks
  4.  * Purpose   : Pattern Mask object - Will identify strings of a
  5.  *             particular pattern. Usefull for searching for
  6.  *             particular filetypes amongst many other things.
  7.  * Author    : J W Gregg, J@magic01.freeserve.co.uk - 1999
  8.  * History   : V1.1 See corresponding footnotes for Use.
  9.  * Copywright: John Gregg, 1999
  10.  *
  11.  * This Unit is a replacement for Borlands Masks unit which is
  12.  * only available with the client server version. :(
  13.  * It offers the Class TMask - which includes the method Match().
  14.  *
  15.  * This unit and it's contents are FreeWare, you may use them freely
  16.  * commercialy or otherwise, however, I would like a full version
  17.  * of the program in which it is used or partly used. You are Not
  18.  * allowed though, to alter any of the code in this unit without
  19.  * permission by me, J W Gregg, except the constant parameter named
  20.  * MAXPARTS.
  21.  *
  22.  * Any bug reports or request etc, to me at the above email address.
  23.  *
  24.  ****************************************************************}
  25.  
  26. unit Masks;
  27.  
  28. interface
  29.  
  30. uses sysutils;
  31.  
  32. Const
  33.    MAXPARTS = 15; {Change this as you wish, but be aware of mem issues.}
  34.  
  35. type
  36.    EMaskError = class(Exception);
  37.    status = (SUCCESS, FAILURE, _EOF);
  38.    TMask = class;
  39.    func = function(part1, part2: string;
  40.                    mask: TMask;
  41.                    start: integer):status;
  42.  
  43.    TMask = class
  44.      public
  45.       substring: string;
  46.       functionCall: array [1..MAXPARTS] of func;
  47.       piece: array [1..MAXPARTS] of string;
  48.       function Match(txt: string; start: integer): Boolean;
  49.       function Matches(txt: string): Boolean;
  50.       constructor create(pattern: string);
  51.    end;
  52.  
  53.    function isString(part1, part2: string;
  54.                      mask: TMask;
  55.                      start: integer):status;
  56.    function notInSet(part1, part2: string;
  57.                      mask: TMask;
  58.                      start: integer):status;
  59.    function inSet(part1, part2: string;
  60.                   mask: TMask;
  61.                   start: integer):status;
  62.    function isNot(part1, part2: string;
  63.                   mask: TMask;
  64.                   start: integer):status;
  65.    function isAny(part1, part2: string;
  66.                   mask: TMask;
  67.                   start: integer):status;
  68.    function isAbs(part1, part2: string;
  69.                   mask: TMask;
  70.                   start: integer):status;
  71.    function isAnyMultiple(part1, part2: string;
  72.                           mask: TMask;
  73.                           start: integer):status;
  74.    function notOr(part1, part2: string;
  75.                   mask: TMask;
  76.                   start: integer):status;
  77.    function isOr(part1, part2: string;
  78.                  mask: TMask;
  79.                  start: integer):status;
  80.    function EOM(part1, part2: string;
  81.                 mask: TMask;
  82.                 start: integer):status;
  83.  
  84. implementation
  85.  
  86. constructor TMask.create(pattern: string);
  87. var
  88.    pos: integer;
  89.    partNo: integer;
  90.    pieceTxt: string;
  91.    currentPos: integer;
  92.    seperators: set of char;
  93.    done: boolean;
  94.  
  95. begin
  96.    done := False;
  97.    pos := 1;
  98.    partNo := 1;
  99.    seperators := ['[','!','*','?','#','('];
  100.    while not done do
  101.    begin
  102.       currentPos := pos;
  103.       if partNo > MAXPARTS then
  104.          raise EMaskError.create('Error');
  105.       while not((pattern[pos] in seperators) or (pos = length(pattern))) do
  106.          inc(pos);
  107.       if pattern[pos] in seperators then
  108.       begin
  109.          if pos - currentPos >= 1 then
  110.          begin
  111.             functionCall[partNo] := @isString;
  112.             piece[partNo] := copy(pattern, currentPos, pos - currentPos);
  113.             currentPos := pos;
  114.             inc(partNo);
  115.          end;
  116.          case pattern[pos] of
  117.             '[': begin
  118.                     repeat
  119.                       inc(pos);
  120.                     until (pattern[pos] = ']') or (pos >= length(pattern));
  121.                     if pos > length(pattern) then
  122.                        raise EMaskError.create('Error');
  123.                     if pattern[pos] = ']' then
  124.                     begin
  125.                        pieceTxt := copy(pattern, currentPos, (pos - currentPos) + 1);
  126.                        if pieceTxt[2] = '!' then
  127.                        begin
  128.                           if pieceTxt[4] = '-' then
  129.                           begin
  130.                               piece[partNo] := concat(pieceTxt[3],pieceTxt[5]);
  131.                               functionCall[partNo] := @notInSet;
  132.                           end
  133.                           else
  134.                               raise EMaskError.create('Error');
  135.                        end
  136.                        else
  137.                        begin
  138.                           if pieceTxt[3] = '-' then
  139.                           begin
  140.                               piece[partNo] := concat(pieceTxt[2],pieceTxt[4]);
  141.                               functionCall[partNo] := @inSet;
  142.                           end
  143.                           else
  144.                               raise EMaskError.create('Error');
  145.                        end;
  146.                     end
  147.                     else
  148.                       raise EMaskError.create('Error');
  149.                     inc(pos);
  150.                  end;
  151.             '(': begin
  152.                     repeat
  153.                       inc(pos);
  154.                     until (pattern[pos] = ')') or (pos >= length(pattern));
  155.                     if pos > length(pattern) then
  156.                        raise EMaskError.create('Error');
  157.                     if pattern[pos] = ')' then
  158.                     begin
  159.                        pieceTxt := copy(pattern, currentPos, (pos - currentPos) + 1);
  160.                        if pieceTxt[2] = '!' then
  161.                        begin
  162.                           piece[partNo] := copy(pieceTxt, 3, length(pieceTxt) - 3);
  163.                           functionCall[partNo] := @notOr;
  164.                        end
  165.                        else
  166.                        begin
  167.                           piece[partNo] := copy(pieceTxt, 2, length(pieceTxt) - 2);
  168.                           functionCall[partNo] := @isOr;
  169.                        end
  170.                     end
  171.                     else
  172.                       raise EMaskError.create('Error');
  173.                     inc(pos);
  174.                  end;
  175.             '!': begin
  176.                     inc(pos);
  177.                     if pos > length(pattern) then
  178.                        raise EMaskError.create('Error');
  179.                     piece[partNo] := pattern[pos];
  180.                     functionCall[partNo] := @isNot;
  181.                     inc(pos);
  182.                  end;
  183.             '*': begin
  184.                     piece[partNo] := '*';
  185.                     functionCall[partNo] := @isAnyMultiple;
  186.                     inc(pos);
  187.                  end;
  188.             '?': begin
  189.                     piece[partNo] := '?';
  190.                     functionCall[partNo] := @isAny;
  191.                     inc(pos);
  192.                  end;
  193.             '#': begin
  194.                     inc(pos);
  195.                     if pos > length(pattern) then
  196.                        raise EMaskError.create('Error');
  197.                     piece[partNo] := pattern[pos];
  198.                     functionCall[partNo] := @isAbs;
  199.                     inc(pos);
  200.                  end;
  201.          end;
  202.          inc(partNo);
  203.       end
  204.       else
  205.       begin
  206.          functionCall[partNo] := @isString;
  207.          piece[partNo] := copy(pattern, currentPos, pos);
  208.          inc(pos);
  209.          inc(partNo);
  210.       end;
  211.       if Pos > length(pattern) then
  212.       begin
  213.          functionCall[partNo] := @EOM;
  214.          done := True;
  215.       end;
  216.    end;
  217. end;
  218.  
  219. function TMask.Match(txt: string; start: integer): Boolean;
  220. var
  221.    answer: status;
  222.  
  223. begin
  224.    substring := txt;
  225.    try
  226.        answer := functionCall[start](piece[start], substring, self, start);
  227.        if (answer = SUCCESS) and (@functionCall[start] = @isAnyMultiple) then
  228.           answer := _EOF;
  229.        while (answer <> _EOF) and (answer <> FAILURE) do
  230.        begin
  231.            inc(start);
  232.            answer := functionCall[start](piece[start], substring, self, start);
  233.            if (answer = SUCCESS) and (@functionCall[start] = @isAnyMultiple) then
  234.               answer := _EOF;
  235.        end;
  236.        if answer = _EOF then
  237.            Match := True
  238.        else
  239.            Match := False;
  240.    except
  241.        Match := False;
  242.    end
  243. end;
  244.  
  245. function isString(part1, part2: string;
  246.                   mask: TMask;
  247.                   start: integer): status;
  248. var
  249.    x: integer;
  250.    notValid: Boolean;
  251.  
  252. begin
  253.    x := 1;
  254.    notValid := False;
  255.    while (x <= length(part1)) and not(notValid) do
  256.    begin
  257.       if compareStr(part1[x], part2[x]) <> 0 then
  258.          notValid := True;
  259.       inc(x);
  260.    end;
  261.    if notValid then
  262.       isString := FAILURE
  263.    else
  264.    begin
  265.       mask.substring := copy(part2, x, length(part2));
  266.       isString := SUCCESS;
  267.    end;
  268. end;
  269.  
  270. function notInSet(part1, part2: string;
  271.                   mask: TMask;
  272.                   start: integer): status;
  273. var
  274.    s: set of char;
  275.  
  276. begin
  277.    s := [part1[1]..part1[2]];
  278.    if part2[1] in s then
  279.       notInSet := FAILURE
  280.    else
  281.    begin
  282.       mask.substring := copy(part2, 2, length(part2));
  283.       notInSet := SUCCESS;
  284.    end
  285. end;
  286.  
  287. function inSet(part1, part2: string;
  288.                mask: TMask;
  289.                start: integer): status;
  290. var
  291.    s: set of char;
  292.  
  293. begin
  294.    s := [part1[1]..part1[2]];
  295.    if part2[1] in s then
  296.    begin
  297.       mask.substring := copy(part2, 2, length(part2));
  298.       inSet := SUCCESS;
  299.    end
  300.    else
  301.       inSet := FAILURE;
  302. end;
  303.  
  304. function notOr(part1, part2: string;
  305.                mask: TMask;
  306.                start: integer):status;
  307. var
  308.    s: set of char;
  309.    x: integer;
  310.  
  311. begin
  312.    for x := 1 to length(part1) do
  313.       s := s + [part1[x]];
  314.    if part2[1] in s then
  315.       notOr := FAILURE
  316.    else
  317.    begin
  318.       mask.substring := copy(part2, 2, length(part2));
  319.       notOr := SUCCESS;
  320.    end
  321. end;
  322.  
  323. function isOr(part1, part2: string;
  324.               mask: TMask;
  325.               start: integer):status;
  326. var
  327.    s: set of char;
  328.    x: integer;
  329.  
  330. begin
  331.    s := [];
  332.    for x := 1 to length(part1) do
  333.       s := s + [part1[x]];
  334.    if part2[1] in s then
  335.    begin
  336.       mask.substring := copy(part2, 2, length(part2));
  337.       isOr := SUCCESS;
  338.    end
  339.    else
  340.       isOr := FAILURE;
  341. end;
  342.  
  343. function isNot(part1, part2: string;
  344.                mask: TMask;
  345.                start: integer): status;
  346. begin
  347.    if compareStr(part1[1], part2[1]) = 0 then
  348.       isNot := FAILURE
  349.    else
  350.    begin
  351.       mask.substring := copy(part2, 2, length(part2));
  352.       isNot := SUCCESS;
  353.    end
  354. end;
  355.  
  356. function isAny(part1, part2: string;
  357.                mask: TMask;
  358.                start: integer): status;
  359. begin
  360.    mask.substring := copy(part2, 2, length(part1));
  361.    isAny := SUCCESS;
  362. end;
  363.  
  364. function isAbs(part1, part2: string;
  365.                mask: TMask;
  366.                start: integer): status;
  367. begin
  368.    if compareStr(part1[1], part2[1]) = 0 then
  369.    begin
  370.       mask.substring := copy(part2, 2, length(part2));
  371.       isAbs := SUCCESS;
  372.    end
  373.    else
  374.       isAbs := FAILURE;
  375. end;
  376.  
  377. function isAnyMultiple(part1, part2: string;
  378.                        mask: TMask;
  379.                        start: integer): status;
  380. var
  381.    x, y: integer;
  382.    answer: Boolean;
  383.  
  384. begin
  385.    x := 1;
  386.    y := length(part2);
  387.    repeat
  388.       answer := mask.Match(part2, start + 1);
  389.       if not answer then
  390.          part2 := copy(part2, 2, y);
  391.       inc(x);
  392.    until (answer = TRUE) or (x > y);
  393.    if answer then
  394.    begin
  395.       mask.substring := part2;
  396.       isAnyMultiple := SUCCESS;
  397.    end
  398.    else
  399.       isAnyMultiple := FAILURE;
  400. end;
  401.  
  402. function EOM(part1, part2: string;
  403.              mask: TMask;
  404.              start: integer): status;
  405. begin
  406.    EOM := _EOF;
  407. end;
  408.  
  409. function TMask.Matches(txt:string) : boolean;
  410. begin
  411.         result := Match(txt,1);
  412. end;
  413. end.
  414.  
  415. {V1.01 - 17th March 1999 *******************************************************
  416.  
  417.  Several parsing bugs removed, seems to be ok now.
  418.  
  419.  Bug 1: wouldnt interpret correctly ---> *(1234).doc
  420.  Bug 2: couldnt construct ---> *.[a-b](12345)_*
  421.  
  422.  ******************************************************************************}
  423. {V1.0 - 11th March 1999 ********************************************************
  424.  
  425.  Create the object using the constructor: Create( pattern: String);
  426.  
  427.  When used it will identify certain strings using the following criteria
  428.  functions:
  429.  
  430.  1. The number of fuctions within the Pattern Match string passed to the
  431.     constructer must be less than or equal to MAXPARTS (15). For all intents and
  432.     purposes this is all I require at this time, however I'm to include a more
  433.     dynamic allocation structure in later versions, or can in the interim
  434.     increase the value on request.
  435.  2. The Object handles several types of criteria functions within a string,
  436.     these are listed as follows, each with a brief description.
  437.  
  438.     Sets - These are constructed within the string using the '[' character. When
  439.            the parser finds a matching ']' it will proceed to
  440.            analyse the information. If no matching ']' is found, then an
  441.            exception is raised. Valid examples are:-
  442.  
  443.            [a-b] - Is within the range 'a' to 'b'. --> (a, b)
  444.            [v-z] - Is within the range 'v' to 'z'. --> (v, w, x, y, z)
  445.            [!b-z] - Is NOT within the range 'b' to 'z'. -->
  446.                                            (All chars except 'b'..'z' inclusive)
  447.            Note the use of '!' here which logically NOT's the arguments.
  448.            The construct must be exactly as shown, and is valid for 1 character
  449.            only in the string to be matched. Bad examples follow:-
  450.  
  451.            ![a-b]
  452.            [a-b!]
  453.            [ab].
  454.  
  455.     OR's - These are constructed within the string using the '(' character. If
  456.            no matching ')' is found then an exception is raised. Valid examples
  457.            follow:-
  458.  
  459.            (ab) - Is 'a' or 'b'. --> (a, b)
  460.            (abcdef) - Is 'a', 'b'...'f'. --> (a, b, c, d, e, f)
  461.            (!ab) - Is NOT 'a' or 'b'.
  462.  
  463.            The construct rules for Sets generally apply, except here, the parser
  464.            would not appreciate the following constructs:-
  465.  
  466.            (a)
  467.            (!a)
  468.  
  469.            In either case it would interpret the construct as 'a' or ')', in
  470.            other words a second character should be explicitly declared within
  471.            the string so that the OR is carried out correctly.
  472.  
  473.     * -    You can use this to define a list of any characters, of any length,
  474.            including the empty character ''. This is a standard pattern match
  475.            operator on most systems, and behaves exactly the same here.
  476.  
  477.     ? -    This is straight forward and just means any character. Note this
  478.            does not include the empty character.
  479.  
  480.     ! -    Use this alongside any character and it means NOT that character.
  481.            Valid uses follow:-
  482.  
  483.            !a
  484.            !z
  485.  
  486.            Note a character must follow the operater, or an exception will
  487.            occur. Invalid expressions follow:-
  488.  
  489.            !
  490.            ![a-b]
  491.            !(ab).
  492.  
  493.     # -    This character allows you to explicitly define other operators,
  494.            including itself, as ordinary characters. Valid ops follow:-
  495.  
  496.            #[
  497.            #!
  498.            ##.
  499.  
  500.            Here a character should be explicitly delared after this operator,
  501.            or you could get silly results.
  502.  
  503.     Literal strings can be iserted as is, and are matched identically.
  504.  
  505.     Note: all operations on matching are case sensitive.
  506.  
  507.  When you create the object any errors in the pattern string you pass with the
  508.  constructor should be found and an exception will occur, In this case, it is
  509.  wise then to use try - except blocks unless you are absolutely sure.
  510.  
  511.  Once constructed then, there are two things you can do with this object:-
  512.  
  513.  1. Match the pattern with another string: Use the method
  514.             function Match(txt: string, 1): Boolean
  515.     Where a boolean is returned indicating whether the string matched the
  516.     pattern String, which was passed to the constructor, or not.
  517.     Always Use the value 1 for the second parameter.
  518.  
  519.  2. Call the method Free to release the object.
  520.  
  521.  Note this Object is fast since half the work is done on construction
  522.  of the object. It doesnt use stack intensive recursion either.
  523.  
  524.  Some valid examples:
  525.  
  526.  1. [a-c]!a(abcdef)*.txt
  527.     match = cockie.txt, bed.txt etc
  528.     not match = cac.txt etc.
  529.  
  530.  2. *.*
  531.  
  532.  ******************************************************************************}
  533.  
  534.