Contributor: SWAG SUPPORT TEAM        

Program search;                                 
{$A+,B-,D-,E+,F-,I+,L-,N-,O-,R-,S-,V-}
{$M 16384,0,655360}


 { Copyright 1990 Trevor J Carlsen Version 1.05  24-07-90                    }
 { This Program may be used and distributed as if it was in the Public Domain}
 { With the following exceptions:                                            }
 {    1.  if you alter it in any way, the copyright notice must not be       }
 {        changed.                                                           }
 {    2.  if you use code excerpts in your own Programs, due credit must be  }
 {        given, along With a copyright notice -                             }
 {        "Parts Copyright 1990 Trevor J Carlsen"                            }
 {    3.  No Charge may be made For any Program using code from this Program.} 

 { SEARCH will scan a File or group of Files and report on all occurrences   }
 { of a particular String or group of Characters. if found the search String }
 { will be displayed along With the 79 Characters preceding it and the 79    }
 { Characters following the line it is in.  Wild cards may be used in the    }
 { Filenames to be searched.                                                 }
 
 { if you find this Program useful here is the author's contact address -    }          
         
 {      Trevor J Carlsen                                                     }          
 {      PO Box 568                                                           }          
 {      Port Hedland Western Australia 6721                                  }          
 {      Voice 61 [0]91 72 2026                                               }          
 {      Data  61 [0]91 72 2569                                               }          


 
Uses
  Dos,
  tpString,  { Turbo Power's String handling library.  Procedures and        }
             { Functions used from this Unit are -                           }
             {       BMSearch       THESE ARE in THE SOURCE\MISC DIRECtoRY   }
             {       BMSearchUC                                              }
             {       BMMakeTable                                             }
             {       StUpCase                                                }
  tctimer;   { A little timing routine - not needed if lines (**) removed.   }
  
Const
  bufflen     = 65000;  { Do not increase this buffer size . Ok to decrease. }
  searchlen   = bufflen;
  copyright1  = 'SEARCH - version 1.05 Copyright 1990 Trevor Carlsen';
  copyright2  = 'All rights reserved.';

Type
  str79       = String[79];
  bufferType  = Array[0..bufflen] of Byte;
  buffptr     = ^bufferType;

Const
  space       = #32;
  quote       = #34;
  comma       = #44;
  CaseSensitive : Boolean = True;       { default is a Case sensitive search }
Var
  table       : BTable;                           { Boyer-Moore search table }
  buffer      : buffptr;                             { Pointer to new buffer }
  f           : File;
  DisplayStr  : Array[0..3] of str79;
  Filename,
  SrchStr     : String;
  Slen        : Byte Absolute SrchStr;
  
Procedure Asc2Str(Var s, ns; max: Byte);

  { Converts an Array of asciiz Characters to a turbo String                 }
  { For speed the Variable st is  effectively global and it is thereFore     }
  { vitally important that max is no larger than the ns unTyped parameter    }
  { Failure to ensure this can result in unpredictable Program behaviour     }
  
  Var stArray : Array[0..255] of Byte Absolute s;
      st      : String Absolute ns;
      len     : Byte Absolute st;
      
  begin
    move(stArray[0],st[1],max);
    len := max;
  end; { Asc2Str }

Procedure ReportError(e : Byte);
  { Displays a simple instruction screen in the event of insufficient        }
  { parameters or certain other errors                                       }
  begin
    Writeln('SYNTAX:');
    Writeln('SEARCH [-c] [path]Filename searchstr');
    Writeln(' eg:  SEARCH c:\comm\telix\salt.doc "color"');
    Writeln(' or');
    Writeln('      SEARCH c:\comm\telix\salt.doc 13,10,13,10,13,10,13,10');
    Writeln(' or');
    Writeln('      SEARCH -c c:\*.* "MicroSoft"');
    Writeln;
    Writeln('if the -c option is used then a Case insensitive search is used.');
    Writeln('When used the -c option must be the first parameter.');
    halt(e);
  end; { ReportError }

Procedure ParseCommandLine;
  { This Procedure is Really the key to everything as it parses the command  }
  { line to determine what the String being searched For is.  Because the    }
  { wanted String can be entered in literal Form or in ascii codes this will }
  { disect and determine the method used.                                    }
  
  Var
    parstr      : String;                        { contains the command line }
    len         : Byte Absolute parstr;{ will contain the length of cmd line }
    cpos, qpos,
    spos, chval : Byte;
    error       : Integer;
    
  begin { ParseCommandLine}
    parstr    := String(ptr(PrefixSeg,$80)^);         { Get the command line }
    if parstr[1] = space then
      delete(parstr,1,1);  { if the first Character is a space get rid of it }
    spos      := pos(space,parstr);                   { find the first space }
    if spos    = 0 then                   { No spaces which must be an error }
      ReportError(1);   
    
    Filename  := StUpCase(copy(parstr,1,spos-1));  { Filename used as a temp }
    if pos('-C',Filename) = 1 then begin  { Case insensitive search required }
      CaseSensitive := False;
      delete(parstr,1,spos);                   { Get rid of the used portion }
    end; { if pos('-C' }
    spos      := pos(space,parstr);                        { find next space }
    if spos    = 0 then                   { No spaces which must be an error }
      ReportError(1);                     
    Filename  := StUpCase(copy(parstr,1,spos-1));        { Get the File mask }
    delete(parstr,1,spos);                     { Get rid of the used portion }
    
    qpos      := pos(quote,parstr);          { look For the first quote Char }
    if qpos   <> 0 then begin    { quote Char found - so must be quoted Text }
      if parstr[1] <> quote then ReportError(2);  { first Char must be quote }
      delete(parstr,1,1);                       { get rid of the first quote }
      qpos      := pos(quote,parstr);              { and find the next quote }
      if qpos = 0 then ReportError(3);  { no more quotes - so it is an error }
      SrchStr   := copy(parstr,1,qpos-1);        { search String now defined }
    end  { if qpos <> 0 }
    
    else begin                                   { must be using ascii codes }
      Slen      := 0;     
      cpos      := pos(comma,parstr);                     { find first comma }
      if cpos = 0 then cpos := succ(len);{ No comma - so only one ascii code }
      Repeat                                      { create the search String }
        val(copy(parstr,1,pred(cpos)),chval,error);
        if error <> 0 then ReportError(7);   { there is an error so bomb out }
        inc(Slen);
        SrchStr[Slen] := Char(chval);        { add Char to the search String }
        delete(parstr,1,cpos);           { get rid of used portion of parstr }
        cpos  := pos(comma,parstr);                    { find the next comma }
        if cpos = 0 then cpos := succ(len);    { no more commas so last Char }
      Until len = 0;              { Until whole of command line is processed }
    end; { else}
    
    if not CaseSensitive then       { change the Search String to upper Case }
      SrchStr := StUpCase(SrchStr);
  end; { ParseCommandLine }

Function OpenFile(ofn : String): Boolean;  { open a File For BlockRead/Write }
  Var
    error : Word;
  begin { OpenFile}
    assign(f,ofn);
    {$I-} reset(f,1); {$I+}
    error := Ioresult;
    if error <> 0 then
      Writeln('Cannot open ',ofn);
    OpenFile := error = 0;
  end; { OpenFile }

Procedure CloseFile;
  begin
    {$I-}
    Close(f);
    if Ioresult <> 0 then;    { don't worry too much if an error occurs here }
    {$I+}
  end; { CloseFile }

Procedure SearchFile(Var Filename: String);
  { Reads a File into the buffer and then searches that buffer For the wanted}
  { String or Characters.                                                    }
  Var
    x,y,
    count,
    result,
    bufferpos   : Word;
    abspos      : LongInt;
    finished    : Boolean;
    
  begin  { SearchFile}
    BMMakeTable(SrchStr,table);          { Create a Boyer-Moore search table }
    new(buffer);                     { make room on the heap For the buffers }
    {$I-} BlockRead(f,buffer^,searchlen,result); {$I+}  { Fill buffer buffer }
    if Ioresult <> 0 then begin      { error occurred While reading the File }
      CloseFile;
      ReportError(11);
    end; { if Ioresult }
    abspos       := 0;        { Initialise the Absolute File position marker }
    Repeat
      bufferpos      := 0;               { position marker in current buffer }
      count          := 0;               { offset from search starting point }
      finished := (result < searchlen);    { if buffer <> full no more reads }
      
      Repeat                              { Do a BM search For search String }
        if CaseSensitive then                   { do a Case sensitive search }
          count:=BMSearch(buffer^[bufferpos],result-bufferpos,table,SrchStr)
        else                                  { do a Case insensitive search }
          count:=BMSearchUC(buffer^[bufferpos],result-bufferpos,table,SrchStr);
        
        if count <> $FFFF then begin                   { search String found }
          inc(bufferpos,count);        { starting point of SrchStr in buffer }
          DisplayStr[0] := HexL(abspos+bufferpos) +    { hex and decimal pos }
                           Form('  @######',(abspos+bufferpos) * 1.0);
          if bufferpos > 79 then          { there is a line available beFore }
            Asc2Str(buffer^[bufferpos - 79],DisplayStr[1],79)
          else                          { no line available beFore the found }
            DisplayStr[1] := '';               { position so null the String }
          if (bufferpos + 79) < result then       { at least 79 Chars can be }
            Asc2Str(buffer^[bufferpos],DisplayStr[2],79)         { displayed }
          else                         { only display what is left in buffer }
            Asc2Str(buffer^[bufferpos],DisplayStr[2],result - bufferpos);
          if (bufferpos + 158) < result then    { display the line following }
            Asc2Str(buffer^[bufferpos + 79],DisplayStr[3],79)
          else                          { no line following the found String }
            DisplayStr[3] := '';                { so null the display String }
          Writeln;
          Writeln(DisplayStr[0],'   ',Filename);{ display the File locations }
          
          For x := 1 to 3 do begin
            For y := 1 to length(DisplayStr[x]) do{ filter out non-printables}
              if ord(DisplayStr[x][y]) < 32 then DisplayStr[x][y] := '.';
            if length(DisplayStr[x]) <> 0 then   { only display Strings With }
               Writeln(DisplayStr[x]);                       { valid content }
          end; { For x }
          
          inc(bufferpos,Slen);         { no need to check buffer in found st }
        end;  { if count <> $ffff }
        
      Until (bufferpos >= (result-length(SrchStr))) or (count = $ffff);
      
      if not finished then begin       { Fill 'er up again For another round }
        inc(abspos,result - Slen);      { create overlap so no String missed }
        {$I-} seek(f,abspos);
        BlockRead(f,buffer^,searchlen,result); {$I+}
        if Ioresult <> 0 then begin
          CloseFile;
          ReportError(13);
        end;
      end; { if not finished}
    Until finished;
    dispose(buffer);
  end; { SearchFile }

Procedure SearchForFiles;
  Var
    dirinfo : SearchRec;
    FullName: PathStr;
    DirName : DirStr;
    FName   : NameStr;
    ExtName : ExtStr;
    found   : Boolean;
  begin
    FindFirst(Filename,AnyFile,dirinfo);
    found := DosError = 0;
    if not found then begin
      Writeln('Cannot find ',Filename);
      ReportError(255);
    end;
    FSplit(Filename,DirName,FName,ExtName);
    While found do begin
      if (dirinfo.Attr and 24) = 0 then begin
        FullName := DirName + dirinfo.name;
        if OpenFile(FullName) then begin
          SearchFile(FullName);
          CloseFile;
        end;
      end;
      FindNext(dirinfo);
      found := DosError = 0;
    end;
  end; { SearchForFiles }

begin { main}
  (**) StartTimer;
  Writeln(copyright1);
  Writeln(copyright2);
  ParseCommandLine;
  SearchForFiles;
  (**) WriteElapsedTime;
end.