home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0000 - 0009 / ibm0000-0009 / ibm0003.tar / ibm0003 / TPOWER54.ZIP / DEMOSRC.ARC / TFIND.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-07-10  |  10.5 KB  |  369 lines

  1. {$S-,R-,V-,I-,B-,F-}
  2. {Lots of stack space for recursion, heap not used}
  3. {$M 65000,0,0}
  4.  
  5. {*********************************************************}
  6. {*                     TFIND.PAS 5.07                    *}
  7. {*                   Text find utility                   *}
  8. {*     An example program for Turbo Professional 5.0     *}
  9. {*        Copyright (c) TurboPower Software 1987.        *}
  10. {* Portions copyright (c) Sunny Hill Software 1985, 1986 *}
  11. {*     and used under license to TurboPower Software     *}
  12. {*                 All rights reserved.                  *}
  13. {*********************************************************}
  14.  
  15. program TextFind;
  16.   {-Search a group of files, reporting matches to a string}
  17.  
  18. uses
  19.   Dos,
  20.   TPString,
  21.   TPCmdLin,
  22.   TPdos,
  23.   TPAsciiz;
  24.  
  25. const
  26.   BufSize = 8192;            {Size of input file buffer}
  27.   MaxAvoids = 8;             {Max number of extensions to avoid analyzing}
  28.  
  29. type
  30.   TextBuffer = array[1..BufSize] of Char;
  31.   PathName = string[64];
  32.   AvoidArray = array[1..MaxAvoids] of string[3];
  33.  
  34. const
  35.   Avoids : AvoidArray =      {Extensions to avoid during searching}
  36.   ('COM', 'EXE', 'OBJ', 'TPU', 'TPL', 'TPM', 'BIN', 'WKS');
  37.  
  38. var
  39.   StdErr : Text;             {File for screen status}
  40.   TextBuf : TextBuffer;      {For speeding input reads}
  41.   StartDir : PathName;       {Drive:directory where searching starts}
  42.   FileMask : PathName;       {Mask to decide which files to match}
  43.   FileCount : Word;          {Number of files searched}
  44.   LineCount : LongInt;       {Number of lines searched}
  45.   MatchCount : LongInt;      {Number of matches found}
  46.   Recursive : Boolean;       {True to search all subdirectories of StartDir}
  47.   IgnoreCase : Boolean;      {True to ignore case while searching}
  48.   ShowLines : Boolean;       {True to display each line of text that matches}
  49.   ConOut : Boolean;          {True if all output is to screen}
  50.   CheckAvoid : Boolean;      {True to avoid certain file extensions}
  51.   MsgLen : Word;             {Number of characters currently on status line}
  52.   Match : string;            {String to match against}
  53.   BT : BTable;               {Search table for Boyer-Moore}
  54.   CurLine : Asciiz;          {Current line of text being checked}
  55.   StdErrBuf : Char;          {Buffer for writing to StdErr}
  56.   ElTime : LongInt;          {Elapsed time for statistics}
  57.   LastStatLen : Word;        {Length of last line counter for status}
  58.   NextStep : LongInt;        {Next order of magnitude where LastStatLen increases}
  59.  
  60.   procedure ClearMessage;
  61.     {-Clear the message line}
  62.   begin
  63.     if MsgLen > 0 then begin
  64.       Write(StdErr, ^M, CharStr(' ', MsgLen), ^M);
  65.       MsgLen := 0;
  66.     end;
  67.   end;
  68.  
  69.   procedure Message(msg : string);
  70.     {-Write a message to StdErr}
  71.   begin
  72.     Write(StdErr, msg);
  73.     Inc(MsgLen, Length(msg));
  74.   end;
  75.  
  76.   procedure FatalError(msg : string);
  77.     {-Write error message and halt}
  78.   begin
  79.     ClearMessage;
  80.     Message(msg+^M^J);
  81.     Halt(1);
  82.   end;
  83.  
  84.   function Avoid(Ext : PathName) : Boolean;
  85.     {-Return true if ext falls in the AVOIDS list}
  86.   var
  87.     I : Integer;
  88.   begin
  89.     Avoid := False;
  90.     Ext := StUpcase(Ext);
  91.     for I := 1 to MaxAvoids do
  92.       if Avoids[I] = '' then
  93.         Exit
  94.       else if Ext = Avoids[I] then begin
  95.         Avoid := True;
  96.         Exit;
  97.       end;
  98.   end;
  99.  
  100.   procedure UpdateLineCount(Fline : LongInt; Backup : Boolean);
  101.     {-Update the line counter on the status line}
  102.   begin
  103.     if Backup then begin
  104.       Write(StdErr, CharStr(^H, LastStatLen));
  105.       Dec(MsgLen, LastStatLen);
  106.     end;
  107.     Message('('+Long2Str(Fline)+')');
  108.     if Fline >= NextStep then
  109.       repeat
  110.         Inc(LastStatLen);
  111.         NextStep := 10*NextStep;
  112.       until NextStep > Fline;
  113.   end;
  114.  
  115.   procedure SearchFile(FName : PathName);
  116.     {-Search a file for matches to the match string}
  117.   label
  118.     exitpoint;
  119.   var
  120.     f : Text;
  121.     Fline : LongInt;
  122.     Posn : Word;
  123.     Len : Word;
  124.   begin
  125.  
  126.     {See if this file is to be avoided}
  127.     if CheckAvoid then
  128.       if Avoid(JustExtension(FName)) then
  129.         Exit;
  130.  
  131.     if Match = '' then begin
  132.       {Just report the file name}
  133.       WriteLn(Output, FName);
  134.       Exit;
  135.     end;
  136.  
  137.     {Open the file}
  138.     Assign(f, FName);
  139.     Reset(f);
  140.     if IoResult <> 0 then
  141.       Exit;
  142.     SetTextBuf(f, TextBuf, BufSize);
  143.  
  144.     {Status for file we're searching}
  145.     LastStatLen := 3;
  146.     NextStep := 10;
  147.     ClearMessage;
  148.     Message(FName);
  149.     UpdateLineCount(0, False);
  150.     Fline := 00;
  151.  
  152.     while not(eof(f)) do begin
  153.  
  154.       {Read the next line from the file - up to max length of Asciiz}
  155.       if not(ReadLnAsc(f, CurLine)) then
  156.         goto exitpoint;
  157.  
  158.       Inc(LineCount);
  159.       Inc(Fline);
  160.       if Fline and 255 = 0 then
  161.         UpdateLineCount(Fline, True);
  162.  
  163.       {Search the line}
  164.       Len := LenAsc(CurLine);
  165.       if IgnoreCase then
  166.         Posn := BMSearchUC(CurLine, Len, BT, Match)
  167.       else
  168.         Posn := BMSearch(CurLine, Len, BT, Match);
  169.  
  170.       if Posn <> NotFound then begin
  171.         {Found it, write the output}
  172.         Inc(MatchCount);
  173.  
  174.         if ConOut then begin
  175.           {All output going to console}
  176.           UpdateLineCount(Fline, True);
  177.           WriteLn(StdErr);
  178.           MsgLen := 0;
  179.           if ShowLines then begin
  180.             if not(WriteAsc(StdErr, CurLine)) then
  181.               goto exitpoint;
  182.             Write(StdErr, ^M^J^M^J);
  183.             Message(FName);
  184.             UpdateLineCount(Fline, False);
  185.           end else
  186.             goto exitpoint;
  187.         end else begin
  188.           {Only status is written to console}
  189.           if ShowLines then begin
  190.             WriteLn(Output, ^M^J, FName, '(', Fline, ') ', Succ(Posn));
  191.             if not(WriteAsc(Output, CurLine)) then
  192.               goto exitpoint;
  193.             WriteLn(Output);
  194.             if IoResult <> 0 then
  195.               goto exitpoint;
  196.           end else begin
  197.             {Just display the file name and exit}
  198.             WriteLn(Output, FName);
  199.             goto exitpoint;
  200.           end;
  201.         end;
  202.       end;
  203.  
  204.     end;
  205.  
  206. exitpoint:
  207.     Close(f);
  208.     Inc(FileCount);
  209.   end;
  210.  
  211.   procedure SearchDir(FDir : PathName);
  212.     {-search files in one directory}
  213.   var
  214.     Frec : SearchRec;
  215.   begin
  216.  
  217.     {Scan all the files in the current directory}
  218.     findfirst(AddBackSlash(FDir)+FileMask, anyfile, Frec);
  219.     while doserror = 0 do begin
  220.       with Frec do
  221.         if attr and 24 = 0 then
  222.           {Not a subdirectory or volume label}
  223.           SearchFile(FullPathName(AddBackSlash(FDir)+Name));
  224.       {Try again for next file}
  225.       findnext(Frec);
  226.     end;
  227.  
  228.     {Scan the subdirectories of the current directory}
  229.     if Recursive then begin
  230.       findfirst(AddBackSlash(FDir)+'*.*', anyfile, Frec);
  231.       while doserror = 0 do begin
  232.         with Frec do
  233.           if (attr and 16 <> 0) and (Name[1] <> '.') then begin
  234.             {Search subdirectory}
  235.             SearchDir(AddBackSlash(FDir)+Name);
  236.             {Restore DTA}
  237.             SetDta(@Frec);
  238.           end;
  239.         {Try again for next file}
  240.         findnext(Frec);
  241.       end;
  242.     end;
  243.   end;
  244.  
  245.   procedure WriteHelp;
  246.     {-Show a brief help screen and halt}
  247.   begin
  248.     WriteLn('Usage: TFIND [Options] MatchString');
  249.     WriteLn;
  250.     WriteLn('Options:');
  251.     WriteLn('   /C - Make searching case sensitive');
  252.     WriteLn('   /F - Show matching file names only');
  253.     WriteLn('   /N - Exclude no file extensions from search');
  254.     WriteLn('   /R - Recursively search all subdirectories');
  255.     WriteLn('   /S [d:][path\][mask] - Start search in d:path and');
  256.     WriteLn('        search only files matching mask');
  257.     WriteLn;
  258.     WriteLn('MatchString:');
  259.     WriteLn('   If MatchString is empty, TFIND reports all files matching mask.');
  260.     WriteLn('   If surrounded by quotes (""), MatchString may contain white space.');
  261.     WriteLn('   It also may contain ASCII characters specified numerically:');
  262.     WriteLn('     #nnn - ASCII character nnn (decimal)');
  263.     Halt(1);
  264.   end;
  265.  
  266.   procedure Initialize;
  267.     {-Initialize globals}
  268.   begin
  269.     {Open StdErr for status reporting}
  270.     if not(OpenStdDev(StdErr, 2)) then begin
  271.       WriteLn('Error opening StdErr');
  272.       Halt(1);
  273.     end else
  274.       {Force buffer flush every character}
  275.       SetTextBuf(StdErr, StdErrBuf, 1);
  276.  
  277.     {Is standard output going to screen?}
  278.     ConOut := HandleIsConsole(1);
  279.  
  280.     {Set default search parameters}
  281.     StartDir := '';
  282.     FileMask := '*.*';
  283.     Match := '';
  284.     MsgLen := 0;
  285.     FileCount := 0;
  286.     LineCount := 00;
  287.     MatchCount := 00;
  288.     Recursive := False;
  289.     IgnoreCase := True;
  290.     ShowLines := True;
  291.     CheckAvoid := True;
  292.   end;
  293.  
  294.   procedure GetParameters;
  295.     {-Analyze command line for parameters}
  296.   var
  297.     I : Integer;
  298.     Arg : string;
  299.     DirMask : string;
  300.   begin
  301.     DirMask := '';
  302.  
  303.     I := 1;
  304.     while I <= ParamCount do begin
  305.       Arg := ParamStr(I);
  306.       if (Length(Arg) = 2) and ((Arg[1] = '/') or (Arg[1] = '-')) then
  307.         case Upcase(Arg[2]) of
  308.           'C' : IgnoreCase := False;
  309.           'F' : ShowLines := False;
  310.           'N' : CheckAvoid := False;
  311.           'R' : Recursive := True;
  312.           'S' : DirMask := GetArgString(I, False, False);
  313.         else
  314.           FatalError('Unrecognized option: '+Arg);
  315.         end
  316.  
  317.       else if Match = '' then begin
  318.         Dec(I);
  319.         Match := GetArgString(I, True, True);
  320.  
  321.       end else
  322.         FatalError('More than one match string specified');
  323.  
  324.       {On to next parameter}
  325.       Inc(I);
  326.     end;
  327.  
  328.     if IgnoreCase then
  329.       Match := StUpcase(Match);
  330.  
  331.     if Match <> '' then
  332.       {Build the Boyer-Moore search table}
  333.       BMMakeTable(Match, BT);
  334.  
  335.     if DirMask <> '' then begin
  336.       StartDir := StUpcase(JustPathname(DirMask));
  337.       FileMask := StUpcase(JustFilename(DirMask));
  338.     end;
  339.   end;
  340.  
  341.   procedure ShowStats;
  342.     {-Show statistics at the end of run}
  343.   begin
  344.     ClearMessage;
  345.     WriteLn(StdErr, 'Files searched: ', FileCount);
  346.     WriteLn(StdErr, 'Lines searched: ', LineCount);
  347.     WriteLn(StdErr, 'Matches found : ', MatchCount);
  348.     if (LineCount > 0) and (ElTime > 0) then
  349.       WriteLn(StdErr, 'Lines per sec : ', (1000*LineCount) div ElTime);
  350.   end;
  351.  
  352. begin
  353.   Initialize;
  354.   WriteLn(StdErr, 'Text Finder. Copyright (c) 1987 by TurboPower Software. Version 5.07');
  355.   WriteLn(StdErr);
  356.  
  357.   if ParamCount = 0 then
  358.     WriteHelp
  359.   else
  360.     GetParameters;
  361.  
  362.   ElTime := TimeMs;
  363.   SearchDir(StartDir);
  364.   ElTime := TimeMs-ElTime;
  365.  
  366.   if Match <> '' then
  367.     ShowStats;
  368. end.
  369.