home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / MISC.ZIP / TFIND.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-01-31  |  41.0 KB  |  1,136 lines

  1. {$P128}
  2. {$V-}
  3. {$C-}
  4.  
  5. PROGRAM tfind(Output);
  6.  
  7.   {.F-}
  8.   {
  9.     searches a group of files for a text string.
  10.     type TFIND alone to get help screens.
  11.  
  12.     requires Turbo Pascal version 3.0 to compile.
  13.     heap/stack size sets limit on depth of subdirectory searching allowed.
  14.     compile with max heap= A000.
  15.     program does not check for heap-stack collision, but
  16.       Turbo run-time library will complain if that happens.
  17.     there is a tradeoff between number of files in a given directory
  18.       vs. the maximum depth of subdirectory checking.
  19.     currently each level of subdirectory eats about 16K of RAM, on the heap.
  20.     written 8/29/85, Kim Kokkonen, 408-378-3672.
  21.     revised 9/24/85 to use 8088 fast string instructions. runs 5x faster!
  22.  
  23.     FEATURES:
  24.       assembler-aided search algorithm, almost as fast as DOS FIND,
  25.         and does more.
  26.       match pattern up to 255 characters may include any ASCII character
  27.         except #13, #10 and #26 (which will never produce a match).
  28.       8192 character input lines with segmentation (not truncation)
  29.         for longer lines.
  30.       automatic but selectable exclusion of binary files.
  31.       quiet and verbose modes.
  32.       built-in WordStar filter and Uppercasing.
  33.       general purpose recursive file finder (up to 256 files per directory).
  34.       optionally specify a list of files to search.
  35.       redirectable output.
  36.    }
  37.   {.F+}
  38.  
  39. CONST
  40.   copyright:string[80]='TFIND - Text Finder. Copyright (c) 1986, TurboPower Software';
  41.   version:string[4]='1.00';
  42.   maxfiles = 256;             {max number of files searched in a given directory}
  43.   maxavoids = 32;             {max number of file extensions to avoid}
  44.   optionchar = '-';           {character which prefixes options on command line}
  45.   endstr = #0;
  46.   esc = '\';
  47.   lspace = 's';
  48.   ltab = 't';
  49.   lbackspace = 'b';
  50.   ascii = '#';
  51.   BufLen = 4608;              {buffer size for input reads -
  52.                               keep buflen>=2*max line length desired}
  53.  
  54. TYPE
  55.   drivename = STRING[2];
  56.   filename = STRING[12];
  57.   pathname = STRING[64];
  58.   longstring = STRING[255];
  59.   darray = RECORD
  60.              num : Integer;
  61.              arr : ARRAY[1..maxfiles] OF filename;
  62.            END;
  63.   farray = RECORD
  64.              num : Integer;
  65.              arr : ARRAY[1..maxfiles] OF pathname;
  66.            END;
  67.   register = RECORD
  68.                CASE Integer OF
  69.                  1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  70.                  2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  71.              END;
  72.   dtarec = RECORD
  73.              dosnext : ARRAY[1..21] OF Byte;
  74.              attr : Byte;
  75.              ftime, fdate, flsize, fhsize : Integer;
  76.              fullname : ARRAY[1..13] OF Char;
  77.            END;
  78.   avoidset = ARRAY[1..maxavoids] OF STRING[3];
  79.   buffer = ARRAY[1..BufLen] OF Byte;
  80.   pathstring = STRING[64];
  81.  
  82. VAR
  83.   reg : register;
  84.   listfilename, startpath : pathname;
  85.   matchpattern : longstring;
  86.   matchlen : Byte ABSOLUTE matchpattern;
  87.   avoidextension : avoidset;
  88.   avoidnum : Integer;
  89.   lenmatch : Byte;
  90.   readexclude, recursive, verbose, lineoutput, uselistfile, avoid,
  91.   wordstar, casesens, printheader : Boolean;
  92.   dta : dtarec;
  93.   tstart, tstop, grandbytes, grandtotal, grandmatch : Real;
  94.   buf, cleanbuf : buffer;
  95.   err : Text[128];            {non-redirectable status output written here}
  96.   files : farray;
  97.  
  98.   PROCEDURE dohalt(exitcode : Integer);
  99.     {-halt}
  100.   BEGIN
  101.     Halt(exitcode);
  102.   END;                        {dohalt}
  103.  
  104.   FUNCTION fileexists(s : pathname; attr : Integer) : Boolean;
  105.     {-determine whether a file exists with the specified attribute}
  106.   BEGIN
  107.     reg.ah := $4E;
  108.     s[Length(s)+1] := #0;
  109.     reg.ds := Seg(s);
  110.     reg.dx := Ofs(s[1]);
  111.     reg.cx := attr;
  112.     MsDos(reg);
  113.     fileexists := ((reg.flags AND 1) = 0) AND ((dta.attr AND 31) = attr);
  114.   END;                        {fileexists}
  115.  
  116.   PROCEDURE parsepath(VAR start : pathname;
  117.                       VAR dname : drivename;
  118.                       VAR pname : pathname;
  119.                       VAR fname : filename);
  120.     {-parse a full (perhaps incomplete) pathname into component parts}
  121.   VAR
  122.     i : Integer;
  123.   BEGIN
  124.     {get drive name}
  125.     i := Pos(':', start);
  126.     IF i = 0 THEN BEGIN
  127.       dname := '';
  128.       pname := start;
  129.     END ELSE BEGIN
  130.       dname := Copy(start, 1, i);
  131.       IF i = Length(start) THEN pname := '\'
  132.       ELSE pname := Copy(start, i+1, 64);
  133.     END;
  134.  
  135.     {see if wildcard specified}
  136.     i := Pos('*', start)+Pos('?', start);
  137.  
  138.     {separate out filename and pathname}
  139.     IF (i = 0) AND (fileexists(start, 16) OR (pname = '\')) THEN BEGIN
  140.       {start specifies a subdirectory}
  141.       IF pname <> '\' THEN pname := pname+'\';
  142.       fname := '*.*';
  143.     END ELSE BEGIN
  144.       {parse out filename on end}
  145.       i := Length(pname);
  146.       WHILE (i > 0) AND NOT(pname[i] IN [':', '\', '/']) DO i := i-1;
  147.       fname := Copy(pname, i+1, 63);
  148.       pname := Copy(pname, 1, i);
  149.     END;
  150.   END;                        {parsepath}
  151.  
  152.   FUNCTION stupcase(s : longstring) : longstring;
  153.     {-return the uppercase of a string}
  154.   VAR
  155.     i : Byte;
  156.   BEGIN
  157.     FOR i := 1 TO Length(s) DO s[i] := UpCase(s[i]);
  158.     stupcase := s;
  159.   END;                        {stupcase}
  160.  
  161.   FUNCTION breakpressed : Boolean;
  162.     {-true if Break key has been pressed}
  163.     {-note that keypressed function executes int 23 if ^C has been pressed}
  164.   VAR
  165.     c : Char;
  166.     breakdown : Boolean;
  167.   BEGIN
  168.     {check current state}
  169.     breakdown := False;
  170.     WHILE KeyPressed AND NOT(breakdown) DO BEGIN
  171.       Read(Kbd, c);
  172.       IF c = ^c THEN breakdown := True;
  173.     END;
  174.     breakpressed := breakdown;
  175.   END;                        {breakpressed}
  176.  
  177.   PROCEDURE breakhalt;
  178.     {-executed when break is detected}
  179.     {-exit with return code 1}
  180.   BEGIN
  181.     INLINE(
  182.       {exit with a return code of 1}
  183.       $B8/$01/$4C/            {mov ax,4c01}
  184.       $CD/$21                 {int 21}
  185.       );
  186.   END;                        {breakhalt}
  187.  
  188.   PROCEDURE setbreak;
  189.     {-set the ctrl-break address to a process exit handler}
  190.   BEGIN
  191.     reg.ax := $2523;
  192.     reg.ds := CSeg;
  193.     reg.dx := Ofs(breakhalt);
  194.     MsDos(reg);
  195.   END;                        {setbreak}
  196.  
  197.   PROCEDURE setoptions;
  198.     {-read command line and set up options and defaults}
  199.   VAR
  200.     i : Integer;
  201.     c : Char;
  202.     haltsoon, gotmatch : Boolean;
  203.     param : longstring;
  204.  
  205.     PROCEDURE writehelp;
  206.     VAR
  207.       ch : Char;
  208.     BEGIN
  209.       writeln;
  210.       writeln;
  211.       writeln(copyright);
  212.       writeln('All Rights Reserved. Version ',version);
  213.       WriteLn;
  214.       writeln;
  215.       WriteLn('Usage:  TFIND [options] MatchString [output redirection]');
  216.       WriteLn;
  217.       WriteLn('TFIND is used to search a set of files for a specified text string. It is');
  218.       WriteLn('designed specifically for use with text files but will work half-heartedly');
  219.       WriteLn('with binary files. Each <CR><LF> delimited text line is limited to 2048');
  220.       WriteLn('characters. Lines longer than this will be broken into multiple segments, but');
  221.       WriteLn('no text will be lost. Any given match must occur within a single line.');
  222.       WriteLn;
  223.       WriteLn('The MatchString contains the text which TFIND tries to find. It must always');
  224.       WriteLn('be specified on the command line. It may not contain blank space or carriage');
  225.       WriteLn('returns. Use the special symbols below to insert unprintable characters into');
  226.       WriteLn('MatchString. The search string is limited to 255 characters, although in');
  227.       WriteLn('practice it is limited by the DOS command line to ~120. By default, TFIND');
  228.       WriteLn('does all matching in uppercase (it is not case-sensitive), and it applies');
  229.       WriteLn('a high-bit filter so that it works with WordStar files. These may be');
  230.       WriteLn('changed via options below.');
  231.       WriteLn;
  232.       writeln;
  233.       Write('press any key to continue (Q to quit)....'); Read(Kbd, ch);
  234.       IF UpCase(ch) = 'Q' THEN dohalt(2);
  235.       WriteLn;
  236.       WriteLn;
  237.       WriteLn;
  238.       WriteLn;
  239.       WriteLn('If no options are specified, the search will cover all files in the current');
  240.       WriteLn('directory and drive. Options below allow you to specify selected files, start');
  241.       WriteLn('in any directory or drive, or search all subdirectories of the start');
  242.       WriteLn('directory. TFIND is limited to 256 files per directory.');
  243.       WriteLn;
  244.       WriteLn('Because TFIND is not designed for binary files, it can automatically exclude');
  245.       WriteLn('certain file types from its search. You can exclude extensions by using an');
  246.       WriteLn('optional "exclude" file named TFIND.EXC. TFIND automatically looks in the');
  247.       WriteLn('current directory and the root directory of the default drive for this file.');
  248.       WriteLn('If it finds one of these, it will read it. Each line of TFIND.EXC should');
  249.       WriteLn('hold one extension to be excluded from searching. The normal TFIND.EXC file');
  250.       WriteLn('will exclude files with extensions of COM, EXE, OBJ, WKS, and BIN.');
  251.       WriteLn;
  252.       WriteLn('For each string that TFIND matches, it will write out the filename and line');
  253.       WriteLn('number of the match, followed by the matching line. TFIND writes to the');
  254.       WriteLn('standard output, so these results can be redirected to a file, to the printer');
  255.       WriteLn('or through a MORE filter as desired.');
  256.       WriteLn;
  257.       WriteLn;
  258.       WriteLn;
  259.       WriteLn;
  260.       Write('press any key to continue (Q to quit)....'); Read(Kbd, ch);
  261.       IF UpCase(ch) = 'Q' THEN dohalt(2);
  262.       WriteLn;
  263.       WriteLn('Options:');
  264.       WriteLn;
  265.       WriteLn('    -S Pathname  Start search on specified drive and path, or with specified');
  266.       WriteLn('                 files matched. Full DOS wildcards and shorthand supported.');
  267.       WriteLn('    -R           Recursive search. Search down all subdirectories found.');
  268.       WriteLn('    -F ListFile  Search only those files named in ListFile. Files should');
  269.       WriteLn('                 be listed one per line, and may include pathname.');
  270.       WriteLn('    -V           Print info about lines that do NOT match.');
  271.       WriteLn('    -G           Search for Graphics characters (turn off WordStar Filter).');
  272.       WriteLn('    -C           Make Case significant in searching.');
  273.       WriteLn('    -H           Do NOT print Header lines (line # and file name) with match.');
  274.       WriteLn('    -T           Do NOT print matched Text lines, only then matched file name.');
  275.       WriteLn('    -Q           Quiet output mode. Normally TFIND keeps a status line');
  276.       WriteLn('                 running during its search. This status line shows the current');
  277.       WriteLn('                 file and line number as well as the number of matches found.');
  278.       WriteLn('                 This status always goes to the screen and is never');
  279.       WriteLn('                 redirected. -Q mode turns this status line off.');
  280.       WriteLn('    -N           No exclusions are made, that is, the file TFIND.EXC is not');
  281.       WriteLn('                 read.');
  282.       WriteLn;
  283.       WriteLn('Special Characters:');
  284.       WriteLn('    \s  space        \t  tab        \\  backslash        \b  backspace');
  285.       WriteLn('    #nnn any ASCII character nnn (3 digits or terminated with non-numeral)');
  286.       WriteLn;
  287.       Write('press any key to continue (Q to quit)....'); Read(Kbd, ch);
  288.       IF UpCase(ch) = 'Q' THEN dohalt(2);
  289.       WriteLn;
  290.       WriteLn;
  291.       WriteLn;
  292.       WriteLn('Examples:');
  293.       WriteLn;
  294.       WriteLn('    TFIND stupcase');
  295.       WriteLn('      will search all non-excluded files in the current directory for the');
  296.       WriteLn('      string "stupcase". Case will not be significant.');
  297.       WriteLn;
  298.       WriteLn('    TFIND -R stupcase >stupcase.dat');
  299.       WriteLn('      will search all non-excluded files in the current directory and all of');
  300.       WriteLn('      its subdirectories for "stupcase". Results are written to STUPCASE.DAT.');
  301.       WriteLn;
  302.       WriteLn('    TFIND -S c:\pascal\*.pas -R stupcase');
  303.       WriteLn('      looks at all files with extension .PAS starting in the \pascal directory');
  304.       WriteLn('      and working down.');
  305.       WriteLn;
  306.       WriteLn('    TFIND -S a:graph.p -C GraphBackground');
  307.       WriteLn('      looks only at one file, graph.p, for the word "GraphBackground". Case is');
  308.       WriteLn('      significant in making the match.');
  309.       WriteLn;
  310.       WriteLn('    TFIND -S \*.DOC -R Now\sis\sthe\stime\sfor\sall >lpt1');
  311.       WriteLn('      looks at all DOC files on the current drive for the string "Now is the');
  312.       WriteLn('      time for all", and sends its results to LPT1.');
  313.       dohalt(2);
  314.     END;                      {writehelp}
  315.  
  316.     PROCEDURE doerror(message : longstring);
  317.       {-display an error message}
  318.     BEGIN
  319.       WriteLn(err, message);
  320.       haltsoon := True;
  321.     END;                      {doerror}
  322.  
  323.     FUNCTION getccl(arg : longstring; i : Integer; VAR s : longstring) : Boolean;
  324.       {-expand a character class starting at position i of arg into a string s}
  325.       {return true if successful}
  326.  
  327.       PROCEDURE dodash(delim : Char; VAR arg : longstring; VAR i : Integer; VAR s : longstring);
  328.         {-expand the innards of the character class, including dashes}
  329.         {stop when endc is found}
  330.         {return a string s with the expansion}
  331.       VAR
  332.         c : Char;
  333.         j : Integer;
  334.  
  335.         PROCEDURE addstr(c : Char; VAR j : Integer; VAR s : longstring);
  336.           {-append a character c onto string s and increment position}
  337.         BEGIN
  338.           j := j+1;
  339.           s[j] := c;
  340.         END;                  {addstr}
  341.  
  342.         PROCEDURE addcode(VAR arg : longstring; VAR i, j : Integer; VAR s : longstring);
  343.           {-interpret an ASCII character code}
  344.         VAR
  345.           npos, code, cval : Integer;
  346.           numstring : STRING[3];
  347.         BEGIN
  348.           npos := 0; numstring := '';
  349.           WHILE (arg[i+npos] IN ['0'..'9']) AND (npos <= 2) DO BEGIN
  350.             numstring := numstring+arg[i+npos];
  351.             npos := npos+1;
  352.           END;
  353.           Val(numstring, cval, code);
  354.           IF (code = 0) AND (cval >= 0) AND (cval < 254) THEN BEGIN
  355.             {add the char to the set string}
  356.             addstr(Chr(cval), j, s);
  357.             i := i+npos-1;
  358.           END ELSE BEGIN
  359.             {illegal character, just interpret literally}
  360.             WriteLn('WARNING: ASCII character ', Copy(arg, i-1, npos+1), ' not interpreted');
  361.             addstr(ascii, j, s);
  362.             i := i-1;
  363.           END;
  364.         END;                  {addcode}
  365.  
  366.       BEGIN                   {dodash}
  367.         j := 0;
  368.         WHILE (arg[i] <> delim) AND (arg[i] <> endstr) DO BEGIN
  369.           c := arg[i];
  370.           IF (c = esc) THEN BEGIN
  371.             IF (arg[i+1] <> endstr) THEN BEGIN
  372.               i := i+1;
  373.               c := arg[i];
  374.               {replace the special characters}
  375.               IF (c = lspace) THEN addstr(#32, j, s)
  376.               ELSE IF (c = ltab) THEN addstr(#9, j, s)
  377.               ELSE IF (c = lbackspace) THEN addstr(#8, j, s)
  378.               ELSE
  379.                 {add escaped character}
  380.                 addstr(c, j, s);
  381.             END ELSE
  382.               {escape must be the character}
  383.               addstr(esc, j, s);
  384.           END ELSE IF c = ascii THEN BEGIN
  385.             IF (arg[i+1] <> endstr) AND (arg[i+1] IN ['0'..'9']) THEN BEGIN
  386.               i := i+1;
  387.               addcode(arg, i, j, s);
  388.             END ELSE
  389.               {ascii must be the character}
  390.               addstr(c, j, s);
  391.           END ELSE
  392.             {literal character}
  393.             addstr(c, j, s);
  394.           i := i+1;
  395.         END;
  396.         s[0] := Chr(j);
  397.       END;                    {dodash}
  398.  
  399.     BEGIN                     {getccl}
  400.       {expand the potentially symbolic matchpattern}
  401.       dodash(endstr, arg, i, s);
  402.       getccl := (arg[i] = endstr);
  403.     END;                      {getccl}
  404.  
  405.     PROCEDURE getexcludes;
  406.       {-find and read the TFIND.EXC file}
  407.     CONST
  408.       {files searched for excludes, add more if you like}
  409.       name1 : pathname = 'TFIND.EXC';
  410.       name2 : pathname = '\TFIND.EXC';
  411.  
  412.       PROCEDURE readexclude(name : pathname);
  413.         {-open and read the exclude file}
  414.       VAR
  415.         f : Text;
  416.         l : STRING[4];
  417.       BEGIN
  418.         IF verbose THEN
  419.           Write(err, 'reading exclude file ', name);
  420.         Assign(f, name);
  421.         Reset(f);
  422.         WHILE NOT(EoF(f)) DO BEGIN
  423.           ReadLn(f, l);
  424.           IF l <> '' THEN BEGIN
  425.             IF l[1] = '.' THEN
  426.               l := stupcase(Copy(l, 2, 3))
  427.             ELSE
  428.               l := stupcase(Copy(l, 1, 3));
  429.             avoidnum := avoidnum+1;
  430.             avoidextension[avoidnum] := l;
  431.           END;
  432.         END;
  433.         Close(f);
  434.       END;                    {readexclude}
  435.  
  436.     BEGIN
  437.       IF fileexists(name1, 0) THEN readexclude(name1)
  438.       ELSE IF fileexists(name2, 0) THEN readexclude(name2)
  439.       ELSE IF verbose THEN BEGIN
  440.         WriteLn(err, 'WARNING: exclude file not found....');
  441.         WriteLn(Con);
  442.       END;
  443.     END;                      {getexcludes}
  444.  
  445.   BEGIN
  446.     {get options}
  447.     IF ParamCount = 0 THEN
  448.       writehelp
  449.     ELSE BEGIN
  450.       WriteLn(Con);
  451.       {get default directory and disk}
  452.       GetDir(0, startpath);
  453.       {set default flags}
  454.       verbose := True;
  455.       wordstar := True;
  456.       casesens := False;
  457.       avoid := False;
  458.       readexclude := True;
  459.       uselistfile := False;
  460.       avoidnum := 0;
  461.       haltsoon := False;
  462.       gotmatch := False;
  463.       recursive := False;
  464.       printheader := True;
  465.       lineoutput := True;
  466.       grandtotal := 0.0;
  467.       grandmatch := 0.0;
  468.       grandbytes := 0.0;
  469.       i := 1;
  470.       WHILE i <= ParamCount DO BEGIN
  471.         {analyze options}
  472.         param := ParamStr(i);
  473.         IF param[1] = optionchar THEN BEGIN
  474.           {an option}
  475.           IF Length(param) = 2 THEN BEGIN
  476.             c := UpCase(param[2]);
  477.             CASE c OF
  478.               'V' : avoid := True;
  479.               'R' : recursive := True;
  480.               'Q' : verbose := False;
  481.               'G' : wordstar := False;
  482.               'C' : casesens := True;
  483.               'N' : readexclude := False;
  484.               'H' : printheader := False;
  485.               'T' : lineoutput := False;
  486.               'S' : BEGIN     {new start path follows}
  487.                       i := i+1;
  488.                       IF i <= ParamCount THEN
  489.                         startpath := stupcase(ParamStr(i))
  490.                       ELSE
  491.                         doerror('New start path not found....');
  492.                     END;
  493.               'F' : BEGIN
  494.                       uselistfile := True;
  495.                       i := i+1;
  496.                       IF i <= ParamCount THEN
  497.                         listfilename := stupcase(ParamStr(i))
  498.                       ELSE
  499.                         doerror('ListFile name not found....');
  500.                     END;
  501.             END;
  502.           END ELSE
  503.             doerror('Unrecognized command option....'+ParamStr(i));
  504.         END ELSE BEGIN
  505.           {the match pattern}
  506.           IF NOT(gotmatch) THEN BEGIN
  507.             param := ParamStr(i)+endstr;
  508.             IF NOT(getccl(param, 1, matchpattern)) THEN
  509.               doerror('Error during expansion of match string....');
  510.             gotmatch := True;
  511.             lenmatch := Length(matchpattern)-1;
  512.           END ELSE
  513.             doerror('More than one match pattern specified....'+ParamStr(i));
  514.         END;
  515.         i := i+1;
  516.       END;
  517.       IF NOT(gotmatch) THEN
  518.         doerror('No match pattern found....');
  519.       IF NOT(casesens) THEN
  520.         matchpattern := stupcase(matchpattern);
  521.       IF haltsoon THEN BEGIN
  522.         WriteLn(err, 'Type TFIND for help....');
  523.         dohalt(2);
  524.       END;
  525.       IF uselistfile THEN BEGIN
  526.         recursive := False;
  527.         readexclude := False;
  528.       END;
  529.       IF readexclude THEN getexcludes;
  530.     END;
  531.   END;                        {setoptions}
  532.  
  533.   PROCEDURE setdta(VAR dta : dtarec);
  534.     {-set new DTA address}
  535.   BEGIN
  536.     reg.ah := $1A;
  537.     reg.ds := Seg(dta);
  538.     reg.dx := Ofs(dta);
  539.     MsDos(reg);
  540.   END;                        {setdta}
  541.  
  542.   PROCEDURE scantext(startpath : pathname);
  543.     {-get all files in pathname, scan them and output matches}
  544.     {-called recursively in recursive mode}
  545.   VAR
  546.     dirs : darray;
  547.     dname : drivename;
  548.     pname, usepath : pathname;
  549.     fname : filename;
  550.     filnum : Integer;
  551.  
  552.     PROCEDURE parsedta(VAR name, ext : filename);
  553.       {-return a name and extension from a DTA}
  554.     VAR
  555.       i : Byte;
  556.       tempname : filename;
  557.     BEGIN
  558.       i := 1;
  559.       WHILE dta.fullname[i] <> #0 DO i := i+1;
  560.       Move(dta.fullname, tempname[1], i-1);
  561.       tempname[0] := Chr(i-1);
  562.       i := Pos('.', tempname);
  563.       IF i = 0 THEN BEGIN
  564.         name := tempname;
  565.         ext := '';
  566.       END ELSE BEGIN
  567.         name := Copy(tempname, 1, i);
  568.         ext := Copy(tempname, i+1, 3);
  569.       END;
  570.     END;                      {parsedta}
  571.  
  572.     FUNCTION getfirst(attr : Integer; VAR startpath : pathname;
  573.                       VAR name, ext : filename;
  574.                       VAR rightdirattr : Boolean) : Boolean;
  575.       {-return true and a name if first file is found}
  576.     VAR
  577.       foundone : Boolean;
  578.     BEGIN
  579.       reg.ah := $4E;
  580.       reg.ds := Seg(startpath);
  581.       reg.dx := Ofs(startpath[1]);
  582.       reg.cx := attr;
  583.       MsDos(reg);
  584.       foundone := ((reg.flags AND 1) = 0);
  585.       rightdirattr := (dta.attr AND 16) = (attr AND 16);
  586.       IF foundone THEN
  587.         {scan the DTA for the file name and extension}
  588.         parsedta(name, ext);
  589.       getfirst := foundone;
  590.     END;                      {getfirst}
  591.  
  592.     FUNCTION getnext(attr : Integer; VAR name, ext : filename;
  593.                      VAR rightdirattr : Boolean) : Boolean;
  594.       {-return true and a name if another file is found}
  595.     VAR
  596.       foundone : Boolean;
  597.     BEGIN
  598.       reg.ah := $4F;
  599.       reg.ds := Seg(dta);
  600.       reg.dx := Ofs(dta);
  601.       MsDos(reg);
  602.       foundone := ((reg.flags AND 1) = 0);
  603.       rightdirattr := (dta.attr AND 16) = (attr AND 16);
  604.       IF foundone THEN
  605.         {scan the DTA for the file name and extension}
  606.         parsedta(name, ext);
  607.       getnext := foundone;
  608.     END;                      {getnext}
  609.  
  610.     PROCEDURE getfiles(attr : Integer; takeall : Boolean;
  611.                        VAR files : farray;
  612.                        VAR startpath : pathname);
  613.       {-return the files in the files array}
  614.     VAR
  615.       tempname, tempext : filename;
  616.       rightdir : Boolean;
  617.  
  618.       FUNCTION goodext(VAR ext : filename) : Boolean;
  619.         {-return true if ext is not one to avoid}
  620.       LABEL 1;
  621.       VAR
  622.         i : Integer;
  623.       BEGIN
  624.         goodext := True;
  625.         FOR i := 1 TO avoidnum DO
  626.           IF ext = avoidextension[i] THEN BEGIN
  627.             goodext := False;
  628.             GOTO 1;
  629.           END;
  630. 1:      
  631.       END;                    {goodext}
  632.  
  633.       PROCEDURE getlistoffiles;
  634.         {-open and read the listfile, check for file existence}
  635.       VAR
  636.         lfile : Text;
  637.         l : pathname;
  638.       BEGIN
  639.         IF fileexists(listfilename, 0) THEN BEGIN
  640.           Assign(lfile, listfilename);
  641.           Reset(lfile);
  642.           WITH files DO BEGIN
  643.             num := 0;
  644.             WHILE NOT(EoF(lfile)) DO BEGIN
  645.               ReadLn(lfile, l);
  646.               IF fileexists(l, 0) THEN BEGIN
  647.                 IF num < maxfiles THEN BEGIN
  648.                   num := num+1;
  649.                   arr[num] := l;
  650.                 END ELSE
  651.                   WriteLn(err, 'Warning: exceeded file capacity. ignoring ', l);
  652.               END ELSE
  653.                 WriteLn(err, 'Warning: file ', l, ' not found. proceeding....');
  654.             END;
  655.           END;
  656.           Close(lfile);
  657.         END ELSE BEGIN
  658.           WriteLn(err, 'ListFile ', listfilename, ' not found....');
  659.           Halt(2);
  660.         END;
  661.       END;                    {getlistoffiles}
  662.  
  663.     BEGIN
  664.       IF uselistfile THEN
  665.         getlistoffiles
  666.       ELSE WITH files DO BEGIN
  667.           startpath[Length(startpath)+1] := #0;
  668.           num := 0;
  669.           IF getfirst(attr, startpath, tempname, tempext, rightdir) THEN
  670.             REPEAT
  671.               IF rightdir AND (tempname <> '.') AND
  672.               (takeall OR goodext(tempext)) THEN BEGIN
  673.                 num := num+1;
  674.                 arr[num] := tempname+tempext;
  675.               END;
  676.             UNTIL (num = maxfiles) OR NOT(getnext(attr, tempname, tempext, rightdir));
  677.         END;
  678.     END;                      {getfiles}
  679.  
  680.     PROCEDURE getdirs(attr : Integer; takeall : Boolean;
  681.                       VAR files : darray;
  682.                       VAR startpath : pathname);
  683.       {-return the directory names in the dirs array}
  684.     VAR
  685.       tempname, tempext : filename;
  686.       rightdir : Boolean;
  687.     BEGIN
  688.       WITH files DO BEGIN
  689.         startpath[Length(startpath)+1] := #0;
  690.         num := 0;
  691.         IF getfirst(attr, startpath, tempname, tempext, rightdir) THEN
  692.           REPEAT
  693.             IF rightdir AND (tempname <> '.') THEN BEGIN
  694.               num := num+1;
  695.               arr[num] := tempname+tempext;
  696.             END;
  697.           UNTIL (num = maxfiles) OR NOT(getnext(attr, tempname, tempext, rightdir));
  698.       END;
  699.     END;                      {getdirs}
  700.  
  701.     PROCEDURE openfile(fname : pathstring; access : Integer; VAR handle, errcode : Integer);
  702.       {-open a file for reading and return the handle}
  703.     BEGIN
  704.       fname := fname+#0;
  705.       reg.ds := Seg(fname[1]); reg.dx := Ofs(fname[1]);
  706.       reg.ax := $3D00 OR access;
  707.       MsDos(reg);
  708.       IF (reg.flags AND 1) = 1 THEN
  709.         errcode := reg.ax
  710.       ELSE BEGIN
  711.         handle := reg.ax;
  712.         errcode := 0;
  713.       END;
  714.     END;                      {openfile}
  715.  
  716.     PROCEDURE closefile(handle : Integer);
  717.       {-close a file opened by openfile}
  718.     BEGIN
  719.       reg.bx := handle;
  720.       reg.ax := $3E00;
  721.       MsDos(reg);
  722.       IF (reg.flags AND 1) = 1 THEN BEGIN
  723.         WriteLn('problem closing file....');
  724.         dohalt(2);
  725.       END;
  726.     END;                      {closefile}
  727.  
  728.     PROCEDURE matchup(VAR fname : pathname);
  729.       {-scan the file fname looking for the matchpattern}
  730.     VAR
  731.       usepath : pathname;
  732.       handle : Integer;
  733.       lpos, bufofs, bufpos, eolpos, searcheol,
  734.       firstmatch, ccount, lcount, mcount : Integer;
  735.       outline : longstring;
  736.       errcode : Integer;
  737.       matched, endoffile : Boolean;
  738.  
  739.       PROCEDURE BlockRead(inhandle : Integer; VAR b : buffer;
  740.                           bufofs : Integer; VAR count : Integer);
  741.         {-read a chunk of characters from the specified handle}
  742.       BEGIN
  743.         reg.bx := inhandle;
  744.         reg.cx := BufLen-bufofs;
  745.         reg.ds := Seg(b[1]);
  746.         reg.dx := Ofs(b[1])+bufofs;
  747.         reg.ax := $3F00;
  748.         MsDos(reg);
  749.         count := reg.ax+bufofs;
  750.       END;                    {blockread}
  751.  
  752.       PROCEDURE putl(VAR l : longstring);
  753.         {-send a short line to the standard output}
  754.       VAR
  755.         len : Byte ABSOLUTE l;
  756.         tlen : Byte;
  757.       BEGIN
  758.         tlen := len+2; l[len+1] := #13; l[tlen] := #10;
  759.         reg.bx := 1;
  760.         reg.cx := tlen;
  761.         reg.ds := Seg(l[1]);
  762.         reg.dx := Ofs(l[1]);
  763.         reg.ah := $40;
  764.         MsDos(reg);
  765.         IF (reg.flags AND 1) = 1 THEN BEGIN
  766.           WriteLn('ERROR: cannot write to output device....');
  767.           dohalt(2);
  768.         END;
  769.         IF reg.ax <> tlen THEN BEGIN
  770.           WriteLn('ERROR: disk full....');
  771.           dohalt(2);
  772.         END;
  773.       END;                    {putl}
  774.  
  775.       PROCEDURE writeline(handle, lstart, lstop : Integer);
  776.         {-send a buffer section to the specified file or device}
  777.       BEGIN
  778.         reg.bx := handle;
  779.         reg.cx := Succ(lstop-lstart);
  780.         reg.ds := Seg(cleanbuf);
  781.         reg.dx := Pred(Ofs(cleanbuf)+lstart);
  782.         reg.ax := $4000;
  783.         MsDos(reg);
  784.         IF (reg.flags AND 1) = 1 THEN BEGIN
  785.           WriteLn(Con);
  786.           WriteLn(err, 'ERROR during write....');
  787.           dohalt(2);
  788.         END;
  789.       END;                    {writeline}
  790.  
  791.       PROCEDURE writeoutput(lcount : Integer; VAR usepath : pathname;
  792.                             lstart, lstop : Integer);
  793.         {-write output for selected lines}
  794.       VAR
  795.         lstring : filename;
  796.       BEGIN
  797.         IF verbose THEN BEGIN
  798.           Write(err, ^M); ClrEol;
  799.         END;
  800.         IF printheader THEN
  801.           if lineoutput then begin
  802.             Str(lcount:7, lstring);
  803.             outline := '['+lstring+'] '+usepath;
  804.             putl(outline);
  805.           end else
  806.             putl(usepath);
  807.         IF lineoutput THEN BEGIN
  808.           writeline(1, lstart, lstop);
  809.           outline := '';
  810.           putl(outline);
  811.           putl(outline);
  812.         END;
  813.         mcount := Succ(mcount);
  814.         IF verbose THEN Write(err, lcount:8, mcount:8, '  ', usepath);
  815.       END;                    {writeoutput}
  816.  
  817.       PROCEDURE checkeof(VAR b : buffer; VAR Count : Integer);
  818.         {-adjust count if #1A found in buffer}
  819.         {count does not include the ^Z}
  820.         {fast way}
  821.       BEGIN
  822.         INLINE(
  823.           $C4/$7E/$04/        {LES    DI,[BP+04]}
  824.           $26/                {ES:    }
  825.           $8B/$0D/            {MOV    CX,[DI]}
  826.           $51/                {PUSH    CX}
  827.           $C4/$7E/$08/        {LES    DI,[BP+08]}
  828.           $B0/$1A/            {MOV    AL,1A}
  829.           $FC/                {CLD    }
  830.           $F2/                {REPNZ    }
  831.           $AE/                {SCASB    }
  832.           $58/                {POP    AX}
  833.           $75/$09/            {JNZ    011A}
  834.           $29/$C8/            {SUB    AX,CX}
  835.           $48/                {DEC    AX}
  836.           $C4/$7E/$04/        {LES    DI,[BP+04]}
  837.           $26/                {ES:    }
  838.           $89/$05             {MOV    [DI],AX}
  839.           );
  840.         {011A:}
  841.       END;                    {checkeof}
  842.  
  843.       PROCEDURE filterbuf(VAR b : buffer; VAR count : Integer);
  844.         {-do wordstar filtering and uppercasing}
  845.         {fast way}
  846.       BEGIN
  847.         IF wordstar THEN BEGIN
  848.           {clear high bits and transform soft chars to hard ones}
  849.           INLINE(
  850.             $C4/$7E/$04/      {LES    DI,[BP+04]}
  851.             $26/              {ES:    }
  852.             $8B/$0D/          {MOV    CX,[DI]}
  853.             $C4/$7E/$08/      {LES    DI,[BP+08]}
  854.             $8B/$F7/          {MOV    SI,DI}
  855.             $FC/              {CLD    }
  856.             {010B:}
  857.             $AC/              {LODSB    }
  858.             $24/$7F/          {AND    AL,7F}
  859.             $3C/$1E/          {CMP    AL,1E}
  860.             $75/$04/          {JNZ    0116}
  861.             $B0/$20/          {MOV    AL,20}
  862.             $EB/$06/          {JMP    011C}
  863.             $3C/$1F/          {CMP    AL,1F}
  864.             $75/$02/          {JNZ    011C}
  865.             $B0/$2D/          {MOV    AL,2D}
  866.             $AA/              {STOSB    }
  867.             $E2/$EC           {LOOP    010B}
  868.             );
  869.         END;
  870.  
  871.         {keep a cased copy of buf for output}
  872.         Move(b, cleanbuf, count);
  873.  
  874.         IF NOT(casesens) THEN BEGIN
  875.           {uppercase the buffer}
  876.           INLINE(
  877.             $C4/$7E/$04/      {LES    DI,[BP+04]}
  878.             $26/              {ES:    }
  879.             $8B/$0D/          {MOV    CX,[DI]}
  880.             $C4/$7E/$08/      {LES    DI,[BP+08]}
  881.             $8B/$F7/          {MOV    SI,DI}
  882.             $FC/              {CLD    }
  883.             $AC/              {LODSB    }
  884.             $8A/$D8/          {MOV    BL,AL}
  885.             $80/$E3/$7F/      {AND    BL,7F}
  886.             $80/$FB/$61/      {CMP    BL,61}
  887.             $7C/$07/          {JL    011D}
  888.             $80/$FB/$7A/      {CMP    BL,7A}
  889.             $7F/$02/          {JG    011D}
  890.             $24/$DF/          {AND    AL,DF}
  891.             $AA/              {STOSB    }
  892.             $E2/$EB           {LOOP    010B}
  893.             );
  894.         END;
  895.       END;                    {filterbuf}
  896.  
  897.       PROCEDURE findeol(VAR b : buffer; Count, bufpos : Integer;
  898.                         VAR eolpos : Integer);
  899.         {-return eolpos as the next <LF> in buffer, or count+1 if not found}
  900.         {fast way}
  901.       BEGIN
  902.         INLINE(
  903.           $C4/$7E/$0C/        {LES    DI,[BP+0C]}
  904.           $8B/$5E/$08/        {MOV    BX,[BP+08]}
  905.           $4B/                {DEC    BX}
  906.           $01/$DF/            {ADD    DI,BX}
  907.           $8B/$4E/$0A/        {MOV    CX,[BP+0A]}
  908.           $51/                {PUSH    CX}
  909.           $29/$D9/            {SUB    CX,BX}
  910.           $B0/$0A/            {MOV    AL,0A}
  911.           $FC/                {CLD    }
  912.           $F2/                {REPNZ    }
  913.           $AE/                {SCASB    }
  914.           $58/                {POP    AX}
  915.           $75/$04/            {JNZ    011A}
  916.           $29/$C8/            {SUB    AX,CX}
  917.           $EB/$01/            {JMP    011B}
  918.           $40/                {INC    AX}
  919.           $C4/$7E/$04/        {LES    DI,[BP+04]}
  920.           $26/                {ES:    }
  921.           $89/$05             {MOV    [DI],AX}
  922.           );
  923.       END;                    {findeol}
  924.  
  925.       PROCEDURE firstchar(VAR buf : buffer; lpos, searcheol : Integer;
  926.                           matchchar : Char; VAR matchpos : Integer);
  927.         {-return matchpos pointing to lead character of match, or as searcheol+1}
  928.         {fast way}
  929.       BEGIN
  930.         INLINE(
  931.           $C4/$7E/$0E/        {LES    DI,[BP+0E]}
  932.           $8B/$5E/$0C/        {MOV    BX,[BP+0C]}
  933.           $01/$DF/            {ADD    DI,BX}
  934.           $8B/$4E/$0A/        {MOV    CX,[BP+0A]}
  935.           $51/                {PUSH    CX}
  936.           $29/$D9/            {SUB    CX,BX}
  937.           $8A/$46/$08/        {MOV    AL,[BP+08]}
  938.           $FC/                {CLD    }
  939.           $F2/                {REPNZ    }
  940.           $AE/                {SCASB    }
  941.           $58/                {POP    AX}
  942.           $75/$04/            {JNZ     011A}
  943.           $29/$C8/            {SUB    AX,CX}
  944.           $EB/$04/            {JMP    011E}
  945.           $8B/$46/$0A/        {MOV    AX,[BP+0A]}
  946.           $40/                {INC    AX}
  947.           $C4/$7E/$04/        {LES    DI,[BP+04]}
  948.           $26/                {ES:    }
  949.           $89/$05             {MOV    [DI],AX}
  950.           );
  951.       END;                    {firstchar}
  952.  
  953.       PROCEDURE matchstr(VAR b : buffer; firstmatch : Integer;
  954.                          VAR matchpattern : longstring; VAR matched : Boolean);
  955.         {-return matched true if a match}
  956.         {fast way}
  957.       BEGIN
  958.         INLINE(
  959.           $C4/$7E/$0E/        {LES    DI,[BP+0E]}
  960.           $8B/$5E/$0C/        {MOV    BX,[BP+0C]}
  961.           $01/$DF/            {ADD    DI,BX}
  962.           $8B/$76/$08/        {MOV    SI,[BP+08]}
  963.           $31/$C9/            {XOR    CX,CX}
  964.           $8A/$0C/            {MOV    CL,[SI]}
  965.           { $FE/$C9/}         {DEC    CL}
  966.           $46/                {INC    SI}
  967.           $46/                {INC    SI}
  968.           $FC/                {CLD    }
  969.           $F3/                {REPZ    }
  970.           $A6/                {CMPSB    }
  971.           $B0/$01/            {MOV    AL,01}
  972.           $E3/$02/            {JCXZ    011C}
  973.           $B0/$00/            {MOV    AL,00}
  974.           $C4/$7E/$04/        {LES    DI,[BP+04]}
  975.           $26/                {ES:    }
  976.           $88/$05             {MOV    [DI],AL}
  977.           );
  978.       END;                    {matchstr}
  979.  
  980.     BEGIN
  981.       IF uselistfile THEN
  982.         usepath := fname+#0
  983.       ELSE
  984.         usepath := dname+pname+fname+#0;
  985.       openfile(usepath, 0, handle, errcode);
  986.  
  987.       IF errcode = 0 THEN BEGIN
  988.         {ready to read and match file}
  989.         lcount := 0; mcount := 0; bufofs := 0;
  990.         IF verbose THEN
  991.           Write(err, lcount:8, mcount:8, '  ', usepath);
  992.         REPEAT
  993.           BlockRead(handle, buf, bufofs, ccount);
  994.           {adjust ccount for first ^Z found in buffer}
  995.           checkeof(buf, ccount);
  996.           endoffile := (ccount <= 0);
  997.           IF NOT(endoffile) THEN BEGIN
  998.             IF breakpressed THEN BEGIN
  999.               closefile(handle);
  1000.               dohalt(1);
  1001.             END;
  1002.             grandbytes := grandbytes+ccount-bufofs;
  1003.             {do wordstar filtering and uppercasing}
  1004.             filterbuf(buf, ccount);
  1005.             bufofs := 0;
  1006.             bufpos := 1;
  1007.             REPEAT
  1008.               {find next EOL (<LF> used)}
  1009.               findeol(buf, ccount, bufpos, eolpos);
  1010.               IF (eolpos > BufLen) AND (bufpos > (BufLen SHR 1)) THEN BEGIN
  1011.                 {eol not found in buffer, continue line into next buffer}
  1012.                 Move(buf[bufpos], buf[1], eolpos-bufpos);
  1013.                 bufofs := eolpos-bufpos;
  1014.                 bufpos := eolpos;
  1015.               END ELSE BEGIN
  1016.                 {end of line found or linebreak forced}
  1017.                 lcount := Succ(lcount);
  1018.                 IF verbose AND ((lcount AND 63) = 0) THEN
  1019.                   Write(err, ^M, lcount:8);
  1020.                 IF eolpos > ccount THEN
  1021.                   {line was broken without finding a <CR><LF>}
  1022.                   searcheol := Pred(eolpos)
  1023.                 ELSE
  1024.                   {don't look at <CR><LF> while searching}
  1025.                   searcheol := eolpos-2;
  1026.                 IF searcheol >= bufpos THEN BEGIN
  1027.                   {nonempty line}
  1028.                   lpos := pred(bufpos);
  1029.                   REPEAT
  1030.                     {see if line has at least the first char of matchpattern}
  1031.                     firstchar(buf, lpos, searcheol, matchpattern[1], firstmatch);
  1032.                     IF firstmatch+lenmatch <= searcheol THEN BEGIN
  1033.                       {found at least the first char at position firstmatch
  1034.                       and have a shot at matching the rest}
  1035.                       matchstr(buf, firstmatch, matchpattern, matched);
  1036.                       IF matched THEN
  1037.                         {skip rest of line}
  1038.                         lpos := Succ(searcheol)
  1039.                       ELSE
  1040.                         {try again after firstmatch}
  1041.                         lpos := Succ(firstmatch);
  1042.                     END ELSE BEGIN
  1043.                       {this line doesn't match}
  1044.                       lpos := Succ(searcheol);
  1045.                       matched := False;
  1046.                     END;
  1047.                   UNTIL lpos > searcheol;
  1048.                 END ELSE
  1049.                   matched := False;
  1050.                 IF (matched AND NOT(avoid)) OR (NOT(matched) AND avoid) THEN begin
  1051.                   writeoutput(lcount, usepath, bufpos, searcheol);
  1052.                   if not(lineoutput) then begin
  1053.                     {show only the first match in a file - force exit here}
  1054.                     eolpos:=ccount;
  1055.                     endoffile:=true;
  1056.                   end;
  1057.                 end;
  1058.               END;
  1059.               bufpos := Succ(eolpos);
  1060.             UNTIL bufpos > ccount;
  1061.           END;
  1062.         UNTIL endoffile;
  1063.         grandtotal := grandtotal+lcount;
  1064.         grandmatch := grandmatch+mcount;
  1065.         closefile(handle);
  1066.         IF verbose THEN BEGIN
  1067.           Write(err, ^M); ClrEol;
  1068.         END;
  1069.       END ELSE BEGIN
  1070.         WriteLn(Con);
  1071.         WriteLn(err, 'PROGRAM ERROR in Matchup: file ', fname, ' not found');
  1072.       END;
  1073.     END;                      {matchup}
  1074.  
  1075.   BEGIN
  1076.     {get a list of all normal, readonly, hidden matching files in startpath}
  1077.     parsepath(startpath, dname, pname, fname);
  1078.     usepath := dname+pname+fname;
  1079.     IF verbose AND NOT(uselistfile) THEN BEGIN
  1080.       Write(err, ^M); ClrEol;
  1081.       Write(err, 'Reading directory of ', usepath);
  1082.     END;
  1083.     getfiles(3, False, files, usepath);
  1084.     IF verbose THEN BEGIN
  1085.       Write(err, ^M); ClrEol;
  1086.     END;
  1087.  
  1088.     {check each file for match pattern}
  1089.     FOR filnum := 1 TO files.num DO matchup(files.arr[filnum]);
  1090.  
  1091.     {look at subdirectories}
  1092.     IF recursive THEN BEGIN
  1093.       {get all subdirectories}
  1094.       usepath := dname+pname+'*.*';
  1095.       getdirs(19, True, dirs, usepath);
  1096.       {look in the subdirectories}
  1097.       FOR filnum := 1 TO dirs.num DO BEGIN
  1098.         {build a pathname to the subdirectory}
  1099.         usepath := dname+pname+dirs.arr[filnum]+'\'+fname;
  1100.         {call recursively}
  1101.         scantext(usepath);
  1102.       END;
  1103.     END;
  1104.   END;                        {scantext}
  1105.  
  1106.   PROCEDURE time(VAR sec : Real);
  1107.     {-return time of day in seconds since midnight}
  1108.   BEGIN
  1109.     reg.ah := $2C;
  1110.     MsDos(reg);
  1111.     sec := 1.0*(reg.dh+60.0*(reg.cl+60.0*reg.ch)+reg.dl/100.0);
  1112.   END;                        {time}
  1113.  
  1114.   PROCEDURE writeresults;
  1115.   BEGIN
  1116.     WriteLn(err, 'lines:   ', grandtotal:7:0);
  1117.     WriteLn(err, 'matches: ', grandmatch:7:0);
  1118.     WriteLn(err, 'bytes:   ', grandbytes:7:0);
  1119.     IF tstop-tstart > 0 THEN BEGIN
  1120.       WriteLn(err, 'line rate: ', (grandtotal/(tstop-tstart)):6:1, ' lines/sec');
  1121.       WriteLn(err, 'byte rate: ', (grandbytes/(tstop-tstart)):6:0, ' bytes/sec');
  1122.     END;
  1123.   END;                        {writeresults}
  1124.  
  1125. BEGIN
  1126.   setdta(dta);
  1127.   Assign(err, 'ERR:');
  1128.   Rewrite(err);
  1129.   setoptions;
  1130.   setbreak;
  1131.   time(tstart);
  1132.   scantext(startpath);
  1133.   time(tstop);
  1134.   if verbose then writeresults;
  1135. END.
  1136.